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=\"$(&apos;#projects&apos;).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>$(&apos;#projects&apos;).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("]]>", "]]&gt;") : 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);
+               }
+       }
+       
+}


Reply via email to