Author: scottbw
Date: Mon Jan 18 12:41:37 2010
New Revision: 900370
URL: http://svn.apache.org/viewvc?rev=900370&view=rev
Log:
This changeset refactors the injection mechanism for start files, and
implements a custom serializer for HtmlCleaner that omits event handler
attributes from escaping as well as script tags and stylesheets;this fixes
WOOKIE-102 and WOOKIE-64. Two sets of unit tests have also been added for the
new classes. Hopefully the new implementation is easier to understand as well
as fixing the issues; there is also scope for replacing HTMLCleaner with
alternative implementations such as Neko or JTidy using the IHtmlProcessor
interface.
Added:
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/HtmlCleanerTest.java
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/HtmlSerializerTest.java
incubator/wookie/trunk/src/org/apache/wookie/util/html/
incubator/wookie/trunk/src/org/apache/wookie/util/html/HtmlCleaner.java
incubator/wookie/trunk/src/org/apache/wookie/util/html/HtmlSerializer.java
incubator/wookie/trunk/src/org/apache/wookie/util/html/IHtmlProcessor.java
incubator/wookie/trunk/src/org/apache/wookie/util/html/StartPageProcessor.java
Removed:
incubator/wookie/trunk/src/org/apache/wookie/util/IStartPageConfiguration.java
incubator/wookie/trunk/src/org/apache/wookie/util/StartPageJSParser.java
Modified:
incubator/wookie/trunk/src/org/apache/wookie/util/WidgetPackageUtils.java
Added:
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/HtmlCleanerTest.java
URL:
http://svn.apache.org/viewvc/incubator/wookie/trunk/src-tests/org/apache/wookie/tests/HtmlCleanerTest.java?rev=900370&view=auto
==============================================================================
---
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/HtmlCleanerTest.java
(added)
+++
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/HtmlCleanerTest.java
Mon Jan 18 12:41:37 2010
@@ -0,0 +1,119 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wookie.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import org.apache.wookie.util.html.HtmlCleaner;
+import org.junit.Test;
+
+
+public class HtmlCleanerTest {
+
+ /**
+ * Tests injecting a script
+ */
+ @Test
+ public void injectScript(){
+ HtmlCleaner cleaner = new HtmlCleaner();
+ String content = "";
+ String out = "";
+ StringWriter writer = new StringWriter();
+ try {
+ cleaner.setReader(new StringReader(content));
+ cleaner.injectScript("test.js");
+ cleaner.process(writer);
+ out = writer.getBuffer().toString();
+ assertEquals("<html><head><script
type=\"text/javascript\" src=\"test.js\"></script></head><body></body></html>",
out);
+ } catch (IOException e) {
+ fail();
+ }
+ }
+
+ /**
+ * tests that user scripts are placed after injected scripts
+ */
+ @Test
+ public void injectScriptWithUserScript(){
+ HtmlCleaner cleaner = new HtmlCleaner();
+ String content = "<head><script type=\"text/javascript\"
src=\"user.js\"></script></head>";
+ String out = "";
+ StringWriter writer = new StringWriter();
+ try {
+ cleaner.setReader(new StringReader(content));
+ cleaner.injectScript("inject.js");
+ cleaner.process(writer);
+ out = writer.getBuffer().toString();
+ assertEquals("<html><head><script
type=\"text/javascript\" src=\"inject.js\"></script><script
type=\"text/javascript\" src=\"user.js\"></script></head><body></body></html>",
out);
+ } catch (IOException e) {
+ fail();
+ }
+ }
+
+ /**
+ * tests injecting stylesheet
+ */
+ @Test
+ public void injectStylesheet(){
+ HtmlCleaner cleaner = new HtmlCleaner();
+ String content = "";
+ String out = "";
+ StringWriter writer = new StringWriter();
+ try {
+ cleaner.setReader(new StringReader(content));
+ cleaner.injectStylesheet("test.css");
+ cleaner.process(writer);
+ out = writer.getBuffer().toString();
+ assertEquals("<html><head><link type=\"text/css\"
rel=\"stylesheet\" href=\"test.css\" /></head><body></body></html>", out);
+ } catch (IOException e) {
+ fail();
+ }
+ }
+
+ @Test (expected = IOException.class)
+ public void nullReader() throws IOException{
+ HtmlCleaner cleaner = new HtmlCleaner();
+ StringWriter writer = new StringWriter();
+ cleaner.setReader(null);
+ cleaner.injectStylesheet("test.css");
+ cleaner.process(writer);
+ fail();
+ }
+ @Test (expected = IOException.class)
+ public void nullContentInReader() throws IOException{
+ HtmlCleaner cleaner = new HtmlCleaner();
+ StringWriter writer = new StringWriter();
+ cleaner.setReader(new FileReader("bogus.html"));
+ cleaner.injectStylesheet("test.css");
+ cleaner.process(writer);
+ }
+
+ @Test (expected = IOException.class)
+ public void nullWriter() throws IOException{
+ HtmlCleaner cleaner = new HtmlCleaner();
+ FileWriter writer = null;
+ cleaner.setReader(new StringReader("test"));
+ cleaner.injectStylesheet("test.css");
+ cleaner.process(writer);
+ }
+
+
+}
Added:
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/HtmlSerializerTest.java
URL:
http://svn.apache.org/viewvc/incubator/wookie/trunk/src-tests/org/apache/wookie/tests/HtmlSerializerTest.java?rev=900370&view=auto
==============================================================================
---
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/HtmlSerializerTest.java
(added)
+++
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/HtmlSerializerTest.java
Mon Jan 18 12:41:37 2010
@@ -0,0 +1,86 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wookie.tests;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.apache.wookie.util.html.HtmlSerializer;
+import org.htmlcleaner.CleanerProperties;
+import org.htmlcleaner.HtmlCleaner;
+import org.htmlcleaner.TagNode;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class HtmlSerializerTest {
+
+ static HtmlCleaner cleaner;
+ static CleanerProperties properties;
+
+ @BeforeClass
+ public static void setup(){
+ cleaner = new org.htmlcleaner.HtmlCleaner();
+ // set cleaner properties
+ properties = cleaner.getProperties();
+ properties.setOmitDoctypeDeclaration(false);
+ properties.setOmitXmlDeclaration(true);
+ properties.setUseCdataForScriptAndStyle(true);
+ properties.setUseEmptyElementTags(false);
+ }
+
+ private String parse(String content){
+ StringWriter writer = new StringWriter();
+ HtmlSerializer ser = new HtmlSerializer(properties);
+ try {
+ TagNode html = cleaner.clean(content);
+ ser.writeXml(html, writer, "UTF-8");
+ return writer.getBuffer().toString();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ // tests the content of event handlers are not escaped
+ @Test
+ public void eventHandlerAttribute(){
+ String out = parse("<body
onload=\"$('#projects').dataTable();\">");
+ assertEquals("<html><head></head><body
onload=\"$('#projects').dataTable();\"></body></html>", out);
+ }
+
+ // tests that other attributes do have content escaped
+ @Test
+ public void otherAttr(){
+ String out = parse("<body
class=\"$('#projects').dataTable();\">");
+ assertEquals("<html><head></head><body
class=\"$('#projects').dataTable();\"></body></html>", out);
+ }
+
+ // tests that script tags are not encoded
+ @Test
+ public void scriptTag(){
+ String out =
parse("<script>$('#projects').dataTable();</script>");
+
assertEquals("<html><head></head><body><script>$('#projects').dataTable();</script></body></html>",
out);
+ }
+
+ // tests that non-script tags are encoded
+ @Test
+ public void sillyTag(){
+ String out =
parse("<silly>$('#projects').dataTable();</silly>");
+
assertEquals("<html><head></head><body><silly>$('#projects').dataTable();</silly></body></html>",
out);
+ }
+
+ // TODO tests for inline CSS
+
+}
Modified:
incubator/wookie/trunk/src/org/apache/wookie/util/WidgetPackageUtils.java
URL:
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/util/WidgetPackageUtils.java?rev=900370&r1=900369&r2=900370&view=diff
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/util/WidgetPackageUtils.java
(original)
+++ incubator/wookie/trunk/src/org/apache/wookie/util/WidgetPackageUtils.java
Mon Jan 18 12:41:37 2010
@@ -41,6 +41,7 @@
import org.apache.wookie.manifestmodel.IManifestModel;
import org.apache.wookie.manifestmodel.IW3CXMLConfiguration;
import org.apache.wookie.manifestmodel.impl.WidgetManifestModel;
+import org.apache.wookie.util.html.StartPageProcessor;
/**
* Utilities for working with Widget packages, i.e. Zip Files with an XML
manifest
@@ -251,9 +252,8 @@
File startFile = new
File(newWidgetFolder.getCanonicalPath() + File.separator + content.getSrc());
String relativestartUrl =
(WidgetPackageUtils.getURLForWidget(localWidgetPath, manifestIdentifier,
content.getSrc()));
content.setSrc(relativestartUrl);
- if(startFile.exists()){
- StartPageJSParser parser = new
StartPageJSParser(startFile, widgetModel);
- parser.doParse();
+ if(startFile.exists()){
+
StartPageProcessor.processStartFile(startFile, widgetModel);
}
}
if (widgetModel.getContentList().isEmpty()){
Added: incubator/wookie/trunk/src/org/apache/wookie/util/html/HtmlCleaner.java
URL:
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/util/html/HtmlCleaner.java?rev=900370&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/util/html/HtmlCleaner.java
(added)
+++ incubator/wookie/trunk/src/org/apache/wookie/util/html/HtmlCleaner.java Mon
Jan 18 12:41:37 2010
@@ -0,0 +1,145 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wookie.util.html;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.htmlcleaner.CleanerProperties;
+import org.htmlcleaner.TagNode;
+
+/**
+ * A HTML processor implemented using HtmlCleaner
+ */
+public class HtmlCleaner implements IHtmlProcessor{
+
+ // The HTML root node
+ private TagNode htmlNode;
+ // The HTML <HEAD> tag
+ private TagNode headNode;
+ // The HtmlCleaner instance
+ private org.htmlcleaner.HtmlCleaner cleaner;
+ // Properties of the cleaner
+ private CleanerProperties properties;
+ // The reader for the HTML to process
+ private Reader reader;
+ // User-specified scripts
+ private ArrayList<TagNode> scriptList;
+
+ /**
+ * Creates a new HtmlCleaner
+ */
+ public HtmlCleaner(){
+ cleaner = new org.htmlcleaner.HtmlCleaner();
+ // set cleaner properties
+ properties = cleaner.getProperties();
+ properties.setOmitDoctypeDeclaration(false);
+ properties.setOmitXmlDeclaration(true);
+ properties.setUseCdataForScriptAndStyle(true);
+ properties.setUseEmptyElementTags(false);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.wookie.util.html.IHtmlProcessor#setFile(java.io.File)
+ */
+ public void setReader(Reader reader) throws IOException{
+ if (reader == null) throw new IOException("Reader was null");
+ this.reader = reader;
+ htmlNode = cleaner.clean(this.reader);
+ headNode = htmlNode.findElementByName(HEAD_TAG, false);
+ // remove widget-specific scripts. These will be replaced
+ // after processing, so that the injected scripts come first
+ removeUserScripts();
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.wookie.util.html.IHtmlProcessor#injectScript(java.lang.String)
+ */
+ public void injectScript(String script) {
+ TagNode js = new TagNode(SCRIPT_TAG);
+ js.addAttribute(TYPE_ATTRIBUTE, TYPE_ATTRIBUTE_VALUE);
+ js.addAttribute(SRC_ATTRIBUTE, script);
+ headNode.addChild(js);
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.wookie.util.html.IHtmlProcessor#injectStylesheet(java.lang.String)
+ */
+ public void injectStylesheet(String stylesheet) {
+ TagNode js = new TagNode(LINK_TAG);
+ js.addAttribute(TYPE_ATTRIBUTE, CSS_TYPE_ATTRIBUTE_VALUE);
+ js.addAttribute(REL_ATTRIBUTE, CSS_REL_ATTRIBUTE_VALUE);
+ js.addAttribute(HREF_ATTRIBUTE, stylesheet);
+ headNode.addChild(js);
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.wookie.util.html.IHtmlProcessor#setCharset(java.lang.String)
+ */
+ public void setCharset(String charset) {
+ // TODO Auto-generated method stub
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.wookie.util.html.IHtmlProcessor#process()
+ */
+ public void process(Writer writer) throws IOException{
+ if (reader == null) throw new IOException("No file has been
specified to process");
+ if (writer == null) throw new IOException("No writer provided");
+ replaceUserScripts();
+ HtmlSerializer ser = new HtmlSerializer(properties);
+ ser.writeXml(htmlNode, writer, "UTF-8");
+ }
+
+
+ /**
+ * Removes any widget-specific scripts and stores them to
+ * be replaced after injecting any dependencies
+ */
+ private void removeUserScripts(){
+ scriptList = new ArrayList<TagNode>();
+ getUserScripts();
+ for(TagNode node : scriptList){
+ headNode.removeChild(node);
+ }
+ }
+
+ /**
+ * Finds any user script imports and saves them to
+ * the scriptList
+ */
+ @SuppressWarnings("unchecked")
+ private void getUserScripts(){
+ List<TagNode> children = headNode.getChildren();
+ for(TagNode child : children){
+ if(child.getName().equals(SCRIPT_TAG) &&
child.getAttributeByName(SRC_ATTRIBUTE)!=null){
+ scriptList.add(child);
+ }
+ }
+ }
+
+ /**
+ * Appends widget-specific scripts to the end of the HEAD tag
+ */
+ private void replaceUserScripts(){
+ for(TagNode node : scriptList){
+ headNode.addChild(node);
+ }
+ }
+
+}
Added:
incubator/wookie/trunk/src/org/apache/wookie/util/html/HtmlSerializer.java
URL:
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/util/html/HtmlSerializer.java?rev=900370&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/util/html/HtmlSerializer.java
(added)
+++ incubator/wookie/trunk/src/org/apache/wookie/util/html/HtmlSerializer.java
Mon Jan 18 12:41:37 2010
@@ -0,0 +1,133 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wookie.util.html;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.htmlcleaner.BaseToken;
+import org.htmlcleaner.CleanerProperties;
+import org.htmlcleaner.ContentToken;
+import org.htmlcleaner.TagNode;
+import org.htmlcleaner.XmlSerializer;
+
+/**
+ * This is a custom serializer for HtmlCleaner that does not escape the
content of
+ * event handler attributes such as "onClick". In other respects it is
identical
+ * to SimpleXmlSerializer.
+ */
+public class HtmlSerializer extends XmlSerializer {
+
+ /**
+ * The set of HTML event handler attributes
+ */
+ private static final String[] EVENT_HANDLERS =
{"onabort","onbeforeunload","onblur","onchange","oncontextmenu","onclick","ondblclick","ondragdrop","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","onerror","onfocus","onkeydown","onkeypress","onkeyup","onload","onmessage","onmousedown","onmouseup","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onmove","onreset","onresize","onscroll","onselect","onstorage","onsubmit","onunload"
};
+
+ public HtmlSerializer(CleanerProperties props){
+ super(props);
+ }
+
+ /**
+ * Checks to see if an attribute should have its value escaped
+ * @param attname the attribute name
+ * @return true if the attribute shouldn't be escaped, otherwise false
+ */
+ protected boolean dontEscapeAttribute(String attname){
+ for (String handler:EVENT_HANDLERS) if
(handler.equalsIgnoreCase(attname)) return true;
+ return false;
+ }
+
+ /**
+ * We only override two lines of this method - see below
+ */
+ @Override
+ protected void serializeOpenTag(TagNode tagNode, Writer writer, boolean
newLine) throws IOException {
+ String tagName = tagNode.getName();
+ Map tagAtttributes = tagNode.getAttributes();
+
+ writer.write("<" + tagName);
+ Iterator it = tagAtttributes.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry entry = (Map.Entry) it.next();
+ String attName = (String) entry.getKey();
+ String attValue = (String) entry.getValue();
+
+ if ( !props.isNamespacesAware() && ("xmlns".equals(attName) ||
attName.startsWith("xmlns:")) ) {
+ continue;
+ }
+ // This is a line we've changed
+ writer.write(" " + attName + "=\"" +
(dontEscapeAttribute(attName)? attValue: escapeXml(attValue)) + "\"");
+ }
+
+ if ( isMinimizedTagSyntax(tagNode) ) {
+ writer.write(" />");
+ if (newLine) {
+ writer.write("\n");
+ }
+ } else if (dontEscape(tagNode)) {
+ // And so is this
+ writer.write(">");
+ } else {
+ writer.write(">");
+ }
+ }
+
+ @Override
+ protected void serializeEndTag(TagNode tagNode, Writer writer, boolean
newLine) throws IOException {
+ String tagName = tagNode.getName();
+
+ // Lets not bother with this shall we?
+ //if (dontEscape(tagNode)) {
+ // writer.write("]]>");
+ //}
+
+ writer.write( "</" + tagName + ">" );
+
+ if (newLine) {
+ writer.write("\n");
+ }
+ }
+
+ /**
+ * This is exactly the same as SimpleXmlSerializer.serialize, however
we have to include it here as it would
+ * inherit from XmlSerializer and miss out our custom
dontEscapeAttribute method
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void serialize(TagNode tagNode, Writer writer) throws
IOException {
+ serializeOpenTag(tagNode, writer, false);
+
+ List tagChildren = tagNode.getChildren();
+ if ( !isMinimizedTagSyntax(tagNode) ) {
+ Iterator childrenIt = tagChildren.iterator();
+ while ( childrenIt.hasNext() ) {
+ Object item = childrenIt.next();
+ if (item != null) {
+ if ( item instanceof ContentToken ) {
+ String content = ((ContentToken) item).getContent();
+ writer.write( dontEscape(tagNode) ?
content.replaceAll("]]>", "]]>") : escapeXml(content) );
+ } else {
+ ((BaseToken)item).serialize(this, writer);
+ }
+ }
+ }
+
+ serializeEndTag(tagNode, writer, false);
+ }
+ }
+
+}
Added:
incubator/wookie/trunk/src/org/apache/wookie/util/html/IHtmlProcessor.java
URL:
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/util/html/IHtmlProcessor.java?rev=900370&view=auto
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/util/html/IHtmlProcessor.java
(added)
+++ incubator/wookie/trunk/src/org/apache/wookie/util/html/IHtmlProcessor.java
Mon Jan 18 12:41:37 2010
@@ -0,0 +1,76 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wookie.util.html;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * Interface for HTML processors used to inject scripts into
+ * start pages and commit other modifications.
+ *
+ * Instances of this class should be used as follows:
+ *
+ * IHtmlProcessor proc = new <em>HtmlProcessorImplClass</em>
+ * proc.setFile(file);
+ * ... set any modifications ...
+ * proc.process();
+ *
+ */
+public interface IHtmlProcessor {
+
+ final String HEAD_TAG = "head";
+ final String SCRIPT_TAG = "script";
+ final String LINK_TAG = "link";
+ final String TYPE_ATTRIBUTE = "type";
+ final String TYPE_ATTRIBUTE_VALUE = "text/javascript";
+ final String CSS_TYPE_ATTRIBUTE_VALUE = "text/css";
+ final String REL_ATTRIBUTE = "rel";
+ final String CSS_REL_ATTRIBUTE_VALUE = "stylesheet";
+ final String SRC_ATTRIBUTE = "src";
+ final String HREF_ATTRIBUTE = "href";
+
+ /**
+ * Set the reader for the source of the content to process.
+ * @param reader the Reader for the HTML content
+ * @throws IOException if the reader does not exist, cannot be read
from, or its content cannot be parsed
+ */
+ public void setReader(Reader reader) throws IOException;
+
+ /**
+ * Injects a JavaScript import with the given src attribute
+ * @param src the src attribute of the Script
+ */
+ public void injectScript(String src);
+
+ /**
+ * Injects a CSS stylesheet reference with the given href attribute
+ * @param href the href attribute of the Link
+ */
+ public void injectStylesheet(String href);
+
+ /**
+ * Sets the charset of the HTML document
+ * @param charset
+ */
+ public void setCharset(String charset);
+
+ /**
+ * Processes the HTML and writes the output to the specified writer
+ * @throws IOException if the file cannot be processed or written
+ */
+ public void process(Writer writer) throws IOException;
+
+}
Added:
incubator/wookie/trunk/src/org/apache/wookie/util/html/StartPageProcessor.java
URL:
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/util/html/StartPageProcessor.java?rev=900370&view=auto
==============================================================================
---
incubator/wookie/trunk/src/org/apache/wookie/util/html/StartPageProcessor.java
(added)
+++
incubator/wookie/trunk/src/org/apache/wookie/util/html/StartPageProcessor.java
Mon Jan 18 12:41:37 2010
@@ -0,0 +1,117 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wookie.util.html;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.wookie.beans.ServerFeature;
+import org.apache.wookie.feature.IFeature;
+import org.apache.wookie.manifestmodel.IFeatureEntity;
+import org.apache.wookie.manifestmodel.IManifestModel;
+
+/**
+ * Processes widget start pages to inject scripts and other assets required
for widget runtime operation.
+ */
+public class StartPageProcessor {
+
+ static final String DWR_UTIL_SRC_VALUE = "/wookie/dwr/util.js";
+ static final String DWR_ENGINE_SRC_VALUE = "/wookie/dwr/engine.js";
+ static final String WIDGET_IMPL_SRC_VALUE =
"/wookie/dwr/interface/WidgetImpl.js";
+ static final String WOOKIE_WRAPPER_SRC_VALUE =
"/wookie/shared/js/wookie-wrapper.js";
+
+ /**
+ * Process a start file ready for deployment.
+ * @param startFile the start file
+ * @param model the widget information model
+ * @throws IOException if there is a problem reading or writing to the
start file, or if the start file cannot be parsed
+ * with the HTML processor used
+ */
+ public static void processStartFile(File startFile, IManifestModel
model) throws Exception{
+ if (startFile == null) throw new Exception("Start file cannot
be processed: file is null");
+ if (!startFile.exists()) throw new Exception("Start file cannot
be processed: file does not exist");
+ if (!(startFile.canWrite()&&startFile.canRead())) throw new
Exception("Start file cannot be processed: read or write permissions missing");
+ if (model == null) throw new Exception("Start file cannot be
processed: widget model is null");
+ IHtmlProcessor engine = new HtmlCleaner();
+ engine.setReader(new FileReader(startFile));
+ addDefaultScripts(engine);
+ addFeatures(engine, model);
+ FileWriter writer = new FileWriter(startFile);
+ engine.process(writer);
+ }
+
+ /**
+ * Injects default wrapper and utilities
+ * @param engine
+ */
+ private static void addDefaultScripts(IHtmlProcessor engine){
+ engine.injectScript(DWR_UTIL_SRC_VALUE);
+ engine.injectScript(DWR_ENGINE_SRC_VALUE);
+ engine.injectScript(WIDGET_IMPL_SRC_VALUE);
+ engine.injectScript(WOOKIE_WRAPPER_SRC_VALUE);
+ }
+
+ /**
+ * Adds features to widget start file by injecting javascript and
stylesheets
+ * required by each supported feature in the model.
+ * @param engine
+ * @param model
+ * @throws Exception if a feature cannot be found and instantiated for
the widget.
+ */
+ private static void addFeatures(IHtmlProcessor engine,IManifestModel
model) throws Exception{
+ for (IFeatureEntity feature: model.getFeatures()){
+ ServerFeature sf =
ServerFeature.findByName(feature.getName());
+ IFeature theFeature =
getFeatureInstanceForName(sf.getClassName());
+ addScripts(engine, theFeature);
+ addStylesheets(engine, theFeature);
+ }
+ }
+
+ /**
+ * Instantiates a feature for a given feature name
+ * @param featureName the name of the feature to be instantiated
+ * @return an IFeature instance
+ * @throws Exception if the feature cannot be instantiated
+ */
+ private static IFeature getFeatureInstanceForName(String featureName)
throws Exception{
+ Class<? extends IFeature> klass = (Class<? extends IFeature>)
Class.forName(featureName);
+ IFeature theFeature = (IFeature) klass.newInstance();
+ return theFeature;
+ }
+
+ /**
+ * Adds scripts for a given feature
+ * @param engine
+ * @param feature
+ */
+ private static void addScripts(IHtmlProcessor engine, IFeature feature){
+ if (feature.scripts() != null){
+ for (String script: feature.scripts())
engine.injectScript(script);
+ }
+ }
+
+ /**
+ * Adds stylesheets for a given feature
+ * @param engine
+ * @param feature
+ */
+ private static void addStylesheets(IHtmlProcessor engine, IFeature
feature){
+ if (feature.stylesheets() != null){
+ for (String style: feature.stylesheets())
engine.injectStylesheet(style);
+ }
+ }
+
+}