You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by we...@apache.org on 2010/03/08 13:46:14 UTC

svn commit: r920300 - in /myfaces/core/trunk: api/src/main/java/javax/faces/context/ api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/ api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/ impl/src/main/java/org/apache/myfaces...

Author: werpu
Date: Mon Mar  8 12:46:14 2010
New Revision: 920300

URL: http://svn.apache.org/viewvc?rev=920300&view=rev
Log:
https://issues.apache.org/jira/browse/MYFACES-2585

Added a new impl which does the necessary CDATA processing, to resolve all pending usecases in this area.
Also fixed a handful of broken throws which used the wrong data type.



Added:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java
    myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java
Modified:
    myfaces/core/trunk/api/src/main/java/javax/faces/context/PartialResponseWriter.java
    myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_LangUtils.js
    myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_ListenerQueue.js
    myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Utils.js
    myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/servlet/PartialViewContextImpl.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlScriptRenderer.java

Modified: myfaces/core/trunk/api/src/main/java/javax/faces/context/PartialResponseWriter.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/context/PartialResponseWriter.java?rev=920300&r1=920299&r2=920300&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/context/PartialResponseWriter.java (original)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/context/PartialResponseWriter.java Mon Mar  8 12:46:14 2010
@@ -36,7 +36,8 @@
     private ResponseWriter _wrapped;
     private boolean hasChanges;
     private String insertType;
-    
+
+   
     /**
      * 
      */
@@ -62,7 +63,7 @@
     {
         if (hasChanges) {
             // Close the <insert> element, if any.
-            
+            //error close the last op if any
             endInsert();
             
             _wrapped.endElement ("changes");
@@ -154,17 +155,27 @@
         _wrapped.endElement ("error-name");
         
         _wrapped.startElement ("error-message", null);
-        _wrapped.startCDATA();
+        startCDATA();
         
         // Leave open; caller will write message.
     }
 
+    @Override
+    public void startCDATA() throws IOException {
+        _wrapped.startCDATA();
+    }
+
+    @Override
+    public void endCDATA() throws IOException {
+        _wrapped.endCDATA();    
+    }
+
     public void startEval() throws IOException
     {
         startChanges();
         
         _wrapped.startElement ("eval", null);
-        _wrapped.startCDATA();
+        startCDATA();
         
         // Leave open; caller will write statements.
     }
@@ -207,7 +218,7 @@
         
         _wrapped.startElement ("update", null);
         _wrapped.writeAttribute ("id", targetId, null);
-        _wrapped.startCDATA();
+        startCDATA();
         
         // Leave open; caller will write content.
     }
@@ -257,7 +268,7 @@
         _wrapped.startElement ("insert", null);
         _wrapped.startElement (insertType, null);
         _wrapped.writeAttribute ("id", targetId, null);
-        _wrapped.startCDATA();
+        startCDATA();
         
         // Leave open; caller will write content.
     }

Modified: myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_LangUtils.js
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_LangUtils.js?rev=920300&r1=920299&r2=920300&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_LangUtils.js (original)
+++ myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_LangUtils.js Mon Mar  8 12:46:14 2010
@@ -141,7 +141,7 @@
         //		Return true if it is a String
 
         if(!myfaces._impl._util._LangUtils.isString(it)) {
-            throw new Exception("myfaces._impl._util._LangUtils.strToArray param not of type string")
+            throw Error("myfaces._impl._util._LangUtils.strToArray param not of type string");
         }
         var resultArr = it.split(splitter);
         for(var cnt = 0; cnt < resultArr.length; cnt++) {
@@ -358,5 +358,5 @@
         }
         return resultArr.join(finalDelimiter);
     };
-   
+
 }

Modified: myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_ListenerQueue.js
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_ListenerQueue.js?rev=920300&r1=920299&r2=920300&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_ListenerQueue.js (original)
+++ myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_ListenerQueue.js Mon Mar  8 12:46:14 2010
@@ -20,7 +20,7 @@
 
     myfaces._impl._util._ListenerQueue.prototype._assertListener = function(/*function*/listener) {
         if("function" != typeof (listener)) {
-            throw new Exception("Error: myfaces._impl._util._ListenerQueue." + arguments.caller.toString() + "Parameter must be of type function");
+            throw Error("Error: myfaces._impl._util._ListenerQueue." + arguments.caller.toString() + "Parameter must be of type function");
         }
     }
 

Modified: myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Utils.js
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Utils.js?rev=920300&r1=920299&r2=920300&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Utils.js (original)
+++ myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/_util/_Utils.js Mon Mar  8 12:46:14 2010
@@ -217,6 +217,26 @@
         item.parentNode.removeChild(item);
     }
 
+   /**
+    * MyFaces specific data post processing
+    * which tries to overcome a limitation
+    * of the used xml format, which embeds the data into CDATA blocks
+    * we map every ]]&gt; into &MYFACES_MAPPED_ENDCDATA; on the server side
+    * and then remap it back into ]]&gt; before posting the cdata stripped
+    * content back into our dom tree
+    * 
+    * @param {String} str
+    */
+    myfaces._impl._util._Utils._cdataPostProcessing = function(str) {
+        var finalCDATAResult = null;
+        if(str.indexOf("&MYFACES_MAPPED_ENDCDATA;") != -1) {
+            finalCDATAResult = String.fromCharCode(myfaces._impl._util._LangUtils.cdataEndDecode(str));
+        } else {
+            finalCDATAResult = str;
+        }
+        return finalCDATAResult;
+    }
+
     /**
      * [STATIC]
      * Replaces HTML elements through others
@@ -230,6 +250,7 @@
         try {
             //for webkit we have to trim otherwise he does not add the adjancent elements correctly
             newTag = myfaces._impl._util._LangUtils.trim(newTag);
+
             // (itemIdToReplace instanceof Node) is NOT compatible with IE8
             var item = (typeof itemIdToReplace == "object") ? itemIdToReplace :
                        myfaces._impl._util._Utils.getElementFromForm(request, context, itemIdToReplace, form);
@@ -577,5 +598,19 @@
         return localOptions.myfaces[configName];
     };
 
+    /**
+     * concatenation routine which concats all childnodes of a node which
+     * contains a set of CDATA blocks to one big string
+     * @param {Node} node the node to concat its blocks for
+     */
+    myfaces._impl._util._Utils.concatCDATABlocks = function(/*Node*/ node) {
+       var cDataBlock = [];
+       // response may contain sevaral blocks
+       for (var i = 0; i < node.childNodes.length; i++) {
+           cDataBlock.push( node.childNodes[i].data);
+       }
+       return cDataBlock.join('');
+    }
+
     myfaces._impl._util._Utils.browserDetection();
 }

Modified: myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js?rev=920300&r1=920299&r2=920300&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js (original)
+++ myfaces/core/trunk/api/src/main/javascript/META-INF/resources/myfaces/_impl/xhrCore/_AjaxResponse.js Mon Mar  8 12:46:14 2010
@@ -174,6 +174,8 @@
                 if (!this.processUpdate(request, context, changes[i])) return false;
             } else if (changes[i].tagName == this._PCMD_EVAL) {
                 //eval is always in CDATA blocks
+                //TODO split eval handling
+
                 myfaces._impl._util._Utils.globalEval(changes[i].firstChild.data);
             } else if (changes[i].tagName == this._PCMD_INSERT) {
                 if (!this.processInsert(request, context, changes[i])) return false;
@@ -219,19 +221,15 @@
                     myfaces._impl._util._Utils.setAttribute(element,"name","javax.faces.ViewState");
                     sourceForm.appendChild(element);
                 }
+                //viewstate cannot have split cdata blocks so we can skip the costlier operation
                 myfaces._impl._util._Utils.setAttribute(element,"value", node.firstChild.nodeValue);
 
             }
             //else??? the latest spec has this omitted we have to wait for the RI which probably covers this
 
         } else {
-            var cDataBlock = [];
-            // response may contain sevaral blocks
-            for (var i = 0; i < node.childNodes.length; i++) {
-                cDataBlock.push( node.childNodes[i].data);
-            }
-            //a join is more efficient that normal string ops!
-            cDataBlock = cDataBlock.join("");
+            // response may contain several blocks
+            var cDataBlock = myfaces._impl._util._Utils.concatCDATABlocks(node);
 
             switch(node.getAttribute('id')) {
                 case "javax.faces.ViewRoot":
@@ -333,6 +331,9 @@
         //either before or after but not two at the same time
         var nodeHolder = null;
         var parentNode = null;
+
+        var cDataBlock = myfaces._impl._util._Utils.concatCDATABlocks(node);
+
         if(beforeSet) {
             beforeId = myfaces._impl._util._LangUtils.trim(beforeId);
             var beforeNode = document.getElementById(beforeId);
@@ -350,8 +351,9 @@
             parentNode = beforeNode.parentNode;
             parentNode.insertBefore(nodeHolder, beforeNode);
 
+
             myfaces._impl._util._Utils.replaceHtmlItem(request, context,
-                nodeHolder, node.firstChild.data, null);
+                nodeHolder, cDataBlock, null);
 
         } else {
             afterId = myfaces._impl._util._LangUtils.trim(afterId);
@@ -364,7 +366,7 @@
             parentNode = afterNode.parentNode;
             parentNode.insertBefore(nodeHolder, afterNode.nextSibling);
             myfaces._impl._util._Utils.replaceHtmlItem(request, context,
-                nodeHolder, node.firstChild.data, null);
+                nodeHolder, cDataBlock, null);
         }
         return true;
     }

Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java?rev=920300&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java Mon Mar  8 12:46:14 2010
@@ -0,0 +1,454 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.myfaces.context;
+
+import org.apache.myfaces.shared_impl.renderkit.html.HtmlResponseWriterImpl;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.PartialResponseWriter;
+import javax.faces.context.ResponseWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * <p/>
+ * Double buffering partial response writer
+ * to take care if embedded CDATA blocks in update delete etc...
+ * <p/>
+ * According to the spec 13.4.4.1 Writing The Partial Response
+ * implementations have to take care to handle nested cdata blocks properly
+ * <p/>
+ * This means we cannot allow nested CDATA
+ * according to the xml spec http://www.w3.org/TR/REC-xml/#sec-cdata-sect
+ * everything within a CDATA block is unparsed except for ]]>
+ * <p/>
+ * Now we have following problem, that CDATA inserts can happen everywhere
+ * not only within the CDATA instructions.
+ * <p/>
+ * What we have to do now is to double buffer CDATA blocks until their end
+ * and also!!! parse their content for CDATA embedding and replace it with an escaped end sequence.
+ * <p/>
+ * Now parsing CDATA embedding is a little bit problematic in case of PPR because
+ * it can happen that someone simply adds a CDATA in a javascript string or somewhere else.
+ * Because he/she is not aware that we wrap the entire content into CDATA.
+ * Simply encoding and decoding of the CDATA is similarly problematic
+ * because the browser then chokes on embedded //<![CDATA[ //]]> sections
+ * <p/>
+ * What we do for now is to simply remove //<![CDATA[ and //]]>
+ * and replace all other pending cdatas with their cdata escapes
+ * ]]&gt; becomes &lt;![CDATA[]]]]&gt;&lt;![CDATA[&gt;
+ * <p/>
+ * If this causes problems in corner cases we also can add a second encoding step in
+ * case of the cdata Javascript comment removal is not enough to cover all corner cases.
+ * <p/>
+ * For now I will only implement this in the impl, due to the spec stating
+ * that implementations are responsible of the correct CDATA handling!
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PartialResponseWriterImpl extends PartialResponseWriter {
+
+    class StackEntry {
+        HtmlResponseWriterImpl writer;
+        StringWriter _doubleBuffer;
+
+        StackEntry(HtmlResponseWriterImpl writer, StringWriter doubleBuffer) {
+            this.writer = writer;
+            _doubleBuffer = doubleBuffer;
+        }
+
+        public HtmlResponseWriterImpl getWriter() {
+            return writer;
+        }
+
+        public void setWriter(HtmlResponseWriterImpl writer) {
+            this.writer = writer;
+        }
+
+        public StringWriter getDoubleBuffer() {
+            return _doubleBuffer;
+        }
+
+        public void setDoubleBuffer(StringWriter doubleBuffer) {
+            _doubleBuffer = doubleBuffer;
+        }
+    }
+
+    HtmlResponseWriterImpl _cdataDoubleBufferWriter = null;
+    StringWriter _doubleBuffer = null;
+    List<StackEntry> _nestingStack = new LinkedList<StackEntry>();
+
+    public PartialResponseWriterImpl(ResponseWriter writer) {
+        super(writer);
+    }
+
+    @Override
+    public void startCDATA() throws IOException {
+        if (!isDoubleBufferEnabled()) {
+            super.startCDATA();
+        } else {
+            _cdataDoubleBufferWriter.write("<![CDATA[");
+        }
+        openDoubleBuffer();
+    }
+
+    private void openDoubleBuffer() {
+        _doubleBuffer = new StringWriter();
+        _cdataDoubleBufferWriter = new HtmlResponseWriterImpl(_doubleBuffer, super.getContentType(), super.getCharacterEncoding());
+
+        StackEntry entry = new StackEntry(_cdataDoubleBufferWriter, _doubleBuffer);
+
+        _nestingStack.add(0, entry);
+    }
+
+    @Override
+    public void endCDATA() throws IOException {
+        closeDoubleBuffer(false);
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.write("]]>");
+        } else {
+            super.endCDATA();
+        }
+    }
+
+    /**
+     * Close double buffer condition
+     * This does either a normal close or a force
+     * close in case of a force close
+     * the entire buffer  is pushed with the post processing
+     * operations into the originating writer
+     *
+     * @param force if set to true the close is a forced close which in any condition
+     *              immediately pushes the buffer content into our writer with a pre operation
+     *              done upfront, in case of a false, the buffer is only swept out if our
+     *              internal CDATA nesting counter is at the nesting depth 1
+     * @throws IOException
+     */
+    private void closeDoubleBuffer(boolean force) throws IOException {
+        if (!isDoubleBufferEnabled()) return;
+        /*
+        * if a force close is issued we reset the condition
+        * to 1 to reach the underlying closing block
+        */
+
+        if (force) {
+            while (!_nestingStack.isEmpty()) {
+                popAndEncodeCurrentStackEntry();
+
+            }
+        } else {
+            popAndEncodeCurrentStackEntry();
+        }
+    }
+
+    private void popAndEncodeCurrentStackEntry() throws IOException {
+        StackEntry elem = _nestingStack.remove(0);
+        StackEntry parent = (_nestingStack.isEmpty()) ? null : _nestingStack.get(0);
+        String result = postProcess(elem);
+        if (parent != null) {
+            _cdataDoubleBufferWriter = parent.getWriter();
+            _doubleBuffer = parent.getDoubleBuffer();
+
+            _cdataDoubleBufferWriter.write(result);
+        } else {
+            _cdataDoubleBufferWriter = null;
+            _doubleBuffer = null;
+
+            //todo write it in a blocks of strings which have
+            //the inherent string length included
+            super.write(result);
+        }
+        elem.getDoubleBuffer().close();
+        elem.getWriter().close();
+    }
+
+    /**
+     * string post processing
+     *
+     * @param currentElement the current writer element
+     * @return the post processed string
+     * @throws IOException in case of an error
+     */
+    private String postProcess(StackEntry currentElement) throws IOException {
+
+        currentElement.getWriter().flush();
+        StringBuffer buffer = currentElement.getDoubleBuffer().getBuffer();
+
+        String resultString = buffer.toString();
+        //section http://www.w3.org/TR/REC-xml/#sec-cdata-sect everything is parsed
+        //until it hits a ]]> hence we need to do some mapping here
+
+        //ok since our maximum string size is Integer.MAX_VALUE (most JVMs use char [] as
+        //representations
+        //we can work on strings directly, instead of having to go through streams
+        //it is absolutely unlikely that we will ever have a buffered stream bigger than that
+        //for our writer!
+        if (resultString.contains("]]>")) {
+
+            //we now first remove pending javascript CDATA blocks
+            //the reason is if we leave them the ppr chokes on them
+            //the syntax from the auto generated CDATA usually is //\s+<![CDATA[
+            resultString = resultString.replaceAll("//\\s*((\\<\\!\\[CDATA\\[)|(\\]\\]\\>))", "");
+            //now to fullfill the xml spec we have to replace all ]] with blocks of cdata
+            resultString = resultString.replaceAll("\\]\\]\\>", "]]><![CDATA[]]]]><![CDATA[>");
+        }
+        return resultString;
+    }
+
+    //--- we need to override ppr specifics to cover the case
+
+    @Override
+    public void endInsert() throws IOException {
+        //we use a force close here to fix possible user CDATA corrections
+        //under normal conditions the force close just processes the same
+        //the underlying close cdata does, but nevertheless
+        //it is better to have an additional layer of fixup
+        closeDoubleBuffer(true);
+        super.endInsert();
+    }
+
+    @Override
+    public void endUpdate() throws IOException {
+        //we use a force close here to fix possible user CDATA corrections
+        //under normal conditions the force close just processes the same
+        //the underlying close cdata does, but nevertheless
+        //it is better to have an additional layer of fixup
+        closeDoubleBuffer(true);
+        super.endUpdate();    //To change body of overridden methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public void endExtension() throws IOException {
+        //we use a force close here to fix possible user CDATA corrections
+        //under normal conditions the force close just processes the same
+        //the underlying close cdata does, but nevertheless
+        //it is better to have an additional layer of fixup
+        closeDoubleBuffer(true);
+        super.endExtension();    //To change body of overridden methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public void endEval() throws IOException {
+        //we use a force close here to fix possible user CDATA corrections
+        //under normal conditions the force close just processes the same
+        //the underlying close cdata does, but nevertheless
+        //it is better to have an additional layer of fixup
+        closeDoubleBuffer(true);
+        super.endEval();    //To change body of overridden methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public void endError() throws IOException {
+        //we use a force close here to fix possible user CDATA corrections
+        //under normal conditions the force close just processes the same
+        //the underlying close cdata does, but nevertheless
+        //it is better to have an additional layer of fixup
+        closeDoubleBuffer(true);
+        super.endError();    //To change body of overridden methods use File | Settings | File Templates.
+    }
+
+    //--- optional delegation method ---
+
+    @Override
+    public void endElement(String name) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.endElement(name);
+        } else {
+            super.endElement(name);
+        }
+    }
+
+    @Override
+    public void writeComment(Object comment) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.writeComment(comment);
+        } else {
+            super.writeComment(comment);
+        }
+    }
+
+    private boolean isDoubleBufferEnabled() {
+        return !_nestingStack.isEmpty();
+    }
+
+    @Override
+    public void startElement(String name, UIComponent component) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.startElement(name, component);
+        } else {
+            super.startElement(name, component);
+        }
+    }
+
+    @Override
+    public void writeText(Object text, String property) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.writeText(text, property);
+        } else {
+            super.writeText(text, property);
+        }
+    }
+
+    @Override
+    public void writeText(char[] text, int off, int len) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.writeText(text, off, len);
+        } else {
+            super.writeText(text, off, len);
+        }
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.writeText(cbuf, off, len);
+        } else {
+            super.write(cbuf, off, len);
+        }
+    }
+
+    @Override
+    public ResponseWriter cloneWithWriter(Writer writer) {
+        return super.cloneWithWriter(writer);
+    }
+
+    @Override
+    public void writeURIAttribute(String name, Object value, String property) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.writeURIAttribute(name, value, property);
+        } else {
+            super.writeURIAttribute(name, value, property);
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        //in case of a close
+        //we have a user error of a final CDATA block
+        //we do some error correction here
+        //since a close is issued we do not care about
+        //a proper closure of the cdata block here anymore
+        if (isDoubleBufferEnabled()) {
+            //we have to properly close all nested cdata stacks
+            //end end our cdata block if open
+            closeDoubleBuffer(true);
+
+            super.endCDATA();
+        }
+        super.close();
+    }
+
+    @Override
+    public void flush() throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.flush();
+        }
+        super.flush();
+    }
+
+    @Override
+    public void writeAttribute(String name, Object value, String property) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.writeAttribute(name, value, property);
+        } else {
+            super.writeAttribute(name, value, property);
+        }
+    }
+
+    @Override
+    public void writeText(Object object, UIComponent component, String string) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.writeText(object, component, string);
+        } else {
+            super.writeText(object, component, string);
+        }
+    }
+
+    @Override
+    public Writer append(char c) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.append(c);
+            return this;
+        } else {
+            return super.append(c);
+        }
+    }
+
+    @Override
+    public Writer append(CharSequence csq, int start, int end) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.append(csq, start, end);
+            return this;
+        } else {
+            return super.append(csq, start, end);
+        }
+    }
+
+    @Override
+    public Writer append(CharSequence csq) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.append(csq);
+            return this;
+        } else {
+            return super.append(csq);
+        }
+    }
+
+    @Override
+    public void write(char[] cbuf) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.write(cbuf);
+        } else {
+            super.write(cbuf);
+        }
+    }
+
+    @Override
+    public void write(int c) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.write(c);
+        } else {
+            super.write(c);
+        }
+    }
+
+    @Override
+    public void write(String str, int off, int len) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.write(str, off, len);
+        } else {
+            super.write(str, off, len);
+        }
+    }
+
+    @Override
+    public void write(String str) throws IOException {
+        if (isDoubleBufferEnabled()) {
+            _cdataDoubleBufferWriter.write(str);
+        } else {
+            super.write(str);
+        }
+    }
+}

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/servlet/PartialViewContextImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/servlet/PartialViewContextImpl.java?rev=920300&r1=920299&r2=920300&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/servlet/PartialViewContextImpl.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/context/servlet/PartialViewContextImpl.java Mon Mar  8 12:46:14 2010
@@ -39,6 +39,7 @@
 import javax.faces.context.ResponseWriter;
 import javax.faces.event.PhaseId;
 
+import org.apache.myfaces.context.PartialResponseWriterImpl;
 import org.apache.myfaces.shared_impl.util.StringUtils;
 import org.apache.myfaces.util.ExternalContextUtils;
 
@@ -274,7 +275,7 @@
             }
             else
             {
-                _partialResponseWriter = new PartialResponseWriter(responseWriter);
+                _partialResponseWriter = new PartialResponseWriterImpl(responseWriter);
             }
         }
         return _partialResponseWriter;

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlScriptRenderer.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlScriptRenderer.java?rev=920300&r1=920299&r2=920300&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlScriptRenderer.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlScriptRenderer.java Mon Mar  8 12:46:14 2010
@@ -24,6 +24,7 @@
 import java.util.Set;
 import java.util.logging.Logger;
 
+import javax.faces.FacesException;
 import javax.faces.application.FacesMessage;
 import javax.faces.application.ProjectStage;
 import javax.faces.application.Resource;
@@ -33,11 +34,7 @@
 import javax.faces.component.UniqueIdVendor;
 import javax.faces.context.FacesContext;
 import javax.faces.context.ResponseWriter;
-import javax.faces.event.ComponentSystemEvent;
-import javax.faces.event.ComponentSystemEventListener;
-import javax.faces.event.ListenerFor;
-import javax.faces.event.ListenersFor;
-import javax.faces.event.PostAddToViewEvent;
+import javax.faces.event.*;
 import javax.faces.render.Renderer;
 
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;
@@ -49,70 +46,82 @@
 
 /**
  * Renderer used by h:outputScript component
- * 
- * @since 2.0
+ *
  * @author Leonardo Uribe (latest modification by $Author$)
  * @version $Revision$ $Date$
+ * @since 2.0
  */
 @JSFRenderer(renderKitId = "HTML_BASIC", family = "javax.faces.Output", type = "javax.faces.resource.Script")
 @ListenerFor(systemEventClass = PostAddToViewEvent.class)
-public class HtmlScriptRenderer extends Renderer implements
-        ComponentSystemEventListener, PartialStateHolder
-{
+public class HtmlScriptRenderer extends Renderer implements PartialStateHolder, ComponentSystemEventListener {
     //private static final Log log = LogFactory.getLog(HtmlScriptRenderer.class);
     private static final Logger log = Logger.getLogger(HtmlScriptRenderer.class.getName());
 
-    public void processEvent(ComponentSystemEvent event)
-    {
-        UIComponent component = event.getComponent();
-        String target = (String) component.getAttributes().get("target");
-        if (target != null)
+    @Override
+    public void processEvent(ComponentSystemEvent event) {
+        if (event instanceof PostAddToViewEvent) {
+            UIComponent component = event.getComponent();
+            String target = (String) component.getAttributes().get("target");
+            if (target != null) {
+                FacesContext facesContext = FacesContext.getCurrentInstance();
+
+                //if (component.getId() != null)
+                //{
+                //    UniqueIdVendor uiv = findParentUniqueIdVendor(component);
+                //
+                //    if ( (!(uiv instanceof UIViewRoot)) && component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
+                //    {
+                //        // The id was set using the closest UniqueIdVendor, but since this one
+                //        // will be relocated, we need to assign an id from the current root.
+                //        // otherwise a duplicate id exception could happen.
+                //        component.setId(facesContext.getViewRoot().createUniqueId(facesContext, null));
+                //    }
+                //}
+
+                facesContext.getViewRoot().addComponentResource(facesContext,
+                        component, target);
+            }
+        }
+
+        if (event instanceof PreRenderViewEvent)
+
         {
-            FacesContext facesContext = FacesContext.getCurrentInstance();
-            
-            //if (component.getId() != null)
-            //{
-            //    UniqueIdVendor uiv = findParentUniqueIdVendor(component);
-            //
-            //    if ( (!(uiv instanceof UIViewRoot)) && component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
-            //    {
-            //        // The id was set using the closest UniqueIdVendor, but since this one
-            //        // will be relocated, we need to assign an id from the current root.
-            //        // otherwise a duplicate id exception could happen.
-            //        component.setId(facesContext.getViewRoot().createUniqueId(facesContext, null));
-            //    }
-            //}
-
-            facesContext.getViewRoot().addComponentResource(facesContext,
-                    component, target);
-        }
-    }
-    
-    //private static UniqueIdVendor findParentUniqueIdVendor(UIComponent component)
-    //{
-    //    UIComponent parent = component.getParent();
-    //
-    //    while (parent != null)
-    //    {
-    //        if (parent instanceof UniqueIdVendor)
-    //        {
-    //            return (UniqueIdVendor) parent;
-    //        }
-    //        parent = parent.getParent();
-    //    }
-    //    return null;
-    //}
+            //TODO target check here
+            UIComponent component = event.getComponent();
+            String target = (String) component.getAttributes().get("target");
+            if (target != null) {
+                FacesContext facesContext = FacesContext.getCurrentInstance();
+                UIComponent uiTarget = facesContext.getViewRoot().getFacet(target);
+                if (uiTarget == null) {
+                    throw new FacesException("Target for component not found");
+                }
+            }
+        }
+    }
+
+//private static UniqueIdVendor findParentUniqueIdVendor(UIComponent component)
+//{
+//    UIComponent parent = component.getParent();
+//
+//    while (parent != null)
+//    {
+//        if (parent instanceof UniqueIdVendor)
+//        {
+//            return (UniqueIdVendor) parent;
+//        }
+//        parent = parent.getParent();
+//    }
+//    return null;
+//}
 
     @Override
-    public boolean getRendersChildren()
-    {
+    public boolean getRendersChildren() {
         return true;
     }
 
     @Override
     public void encodeChildren(FacesContext facesContext, UIComponent component)
-            throws IOException
-    {
+            throws IOException {
         if (facesContext == null)
             throw new NullPointerException("context");
         if (component == null)
@@ -121,19 +130,14 @@
         Map<String, Object> componentAttributesMap = component.getAttributes();
         String resourceName = (String) componentAttributesMap.get(JSFAttr.NAME_ATTR);
         boolean hasChildren = component.getChildCount() > 0;
-        
-        if (resourceName != null && (!"".equals(resourceName)) )
-        {
-            if (hasChildren)
-            {
-                log.info("Component with resourceName "+ resourceName + 
+
+        if (resourceName != null && (!"".equals(resourceName))) {
+            if (hasChildren) {
+                log.info("Component with resourceName " + resourceName +
                         " and child components found. Child components will be ignored.");
             }
-        }
-        else
-        {
-            if (hasChildren)
-            {
+        } else {
+            if (hasChildren) {
                 // Children are encoded as usual. Usually the layout is
                 // <script type="text/javascript">
                 // ...... some javascript .......
@@ -143,56 +147,45 @@
                 writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
                 RendererUtils.renderChildren(facesContext, component);
                 writer.endElement(HTML.SCRIPT_ELEM);
-            }
-            else
-            {
+            } else {
                 if (!facesContext.getApplication().getProjectStage().equals(
-                        ProjectStage.Production))
-                {
-                    facesContext.addMessage(component.getClientId(), 
+                        ProjectStage.Production)) {
+                    facesContext.addMessage(component.getClientId(),
                             new FacesMessage("Component with no name and no body content, so nothing rendered."));
                 }
-            }            
+            }
         }
     }
 
     @Override
     public void encodeEnd(FacesContext facesContext, UIComponent component)
-            throws IOException
-    {
+            throws IOException {
         super.encodeEnd(facesContext, component); //check for NP
-        
+
         Map<String, Object> componentAttributesMap = component.getAttributes();
         String resourceName = (String) componentAttributesMap.get(JSFAttr.NAME_ATTR);
         String libraryName = (String) componentAttributesMap.get(JSFAttr.LIBRARY_ATTR);
 
-        if (resourceName == null)
-        {
-            //log.warn("Trying to encode resource represented by component" + 
+        if (resourceName == null) {
+            //log.warn("Trying to encode resource represented by component" +
             //        component.getClientId() + " without resourceName."+
             //        " It will be silenty ignored.");
             return;
         }
-        if ("".equals(resourceName))
-        {
+        if ("".equals(resourceName)) {
             return;
         }
-        
+
         Resource resource;
-        if (libraryName == null)
-        {
-            if (ResourceUtils.isRenderedScript(facesContext, libraryName, resourceName))
-            {
+        if (libraryName == null) {
+            if (ResourceUtils.isRenderedScript(facesContext, libraryName, resourceName)) {
                 //Resource already founded
                 return;
             }
             resource = facesContext.getApplication().getResourceHandler()
                     .createResource(resourceName);
-        }
-        else
-        {
-            if (ResourceUtils.isRenderedScript(facesContext, libraryName, resourceName))
-            {
+        } else {
+            if (ResourceUtils.isRenderedScript(facesContext, libraryName, resourceName)) {
                 //Resource already founded
                 return;
             }
@@ -200,62 +193,52 @@
                     .createResource(resourceName, libraryName);
 
         }
-        
-        if (resource == null)
-        {
+
+        if (resource == null) {
             //no resource found
-            log.warning("Resource referenced by resourceName "+ resourceName +
+            log.warning("Resource referenced by resourceName " + resourceName +
                     (libraryName == null ? "" : " and libraryName " + libraryName) +
-                    " not found in call to ResourceHandler.createResource."+
+                    " not found in call to ResourceHandler.createResource." +
                     " It will be silenty ignored.");
             return;
-        }
-        else
-        {
+        } else {
             // Rendering resource
-            ResourceUtils.markScriptAsRendered(facesContext,  libraryName, resourceName);
+            ResourceUtils.markScriptAsRendered(facesContext, libraryName, resourceName);
             ResponseWriter writer = facesContext.getResponseWriter();
             writer.startElement(HTML.SCRIPT_ELEM, component);
-            // We can't render the content type, because usually it returns "application/x-javascript"
-            // and this is not compatible with IE. We should force render "text/javascript".
-            writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT , null);
+// We can't render the content type, because usually it returns "application/x-javascript"
+// and this is not compatible with IE. We should force render "text/javascript".
+            writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
             writer.writeURIAttribute(HTML.SRC_ATTR, resource.getRequestPath(), null);
             writer.endElement(HTML.SCRIPT_ELEM);
         }
     }
 
     private boolean _initialStateMarked;
-    
-    public void clearInitialState()
-    {
+
+    public void clearInitialState() {
         _initialStateMarked = false;
     }
 
-    public boolean initialStateMarked()
-    {
+    public boolean initialStateMarked() {
         return _initialStateMarked;
     }
 
-    public void markInitialState()
-    {
+    public void markInitialState() {
         _initialStateMarked = true;
     }
 
-    public boolean isTransient()
-    {
+    public boolean isTransient() {
         return false;
     }
 
-    public void restoreState(FacesContext context, Object state)
-    {
+    public void restoreState(FacesContext context, Object state) {
     }
 
-    public Object saveState(FacesContext context)
-    {
+    public Object saveState(FacesContext context) {
         return null;
     }
 
-    public void setTransient(boolean newTransientValue)
-    {
+    public void setTransient(boolean newTransientValue) {
     }
 }

Added: myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java?rev=920300&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java (added)
+++ myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java Mon Mar  8 12:46:14 2010
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.myfaces.context;
+
+import org.apache.myfaces.shared_impl.renderkit.html.HtmlResponseWriterImpl;
+import org.apache.myfaces.test.base.AbstractJsfTestCase;
+
+import javax.faces.context.PartialResponseWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Test cases for our impl, which tests for the CDATA nesting
+ *
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+
+public class PartialResponseWriterImplTest extends AbstractJsfTestCase {
+
+    static Logger _log = Logger.getLogger(PartialResponseWriterImplTest.class.getName());
+
+    PartialResponseWriterImpl _writer;
+    StringWriter _contentCollector;
+    private static final String STD_UPDATE_RESULT = "<changes><update id=\"blaId\"><![CDATA[testing]]></update>";
+    private static final String CORR_OUTPUT = "checking for correct output: ";
+
+    public PartialResponseWriterImplTest() {
+        super("PartialResponseWriterImplTest");
+    }
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        _contentCollector = new StringWriter(100);
+    }
+
+    public void testBasicWriteTest() {
+        _writer = createTestProbe();
+        try {
+            //_writer.startCDATA();
+            //_writer.startCDATA();
+            _writer.write("testing");
+            // _writer.endCDATA();
+            // _writer.endCDATA();
+            _writer.flush();
+            _writer.close();
+            assertTrue(CORR_OUTPUT, _contentCollector.toString().equals("testing"));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+    public void teststandardNestingTest() {
+        _writer = createTestProbe();
+        try {
+            //_writer.startCDATA();
+            _writer.startCDATA();
+            _writer.write("testing");
+            _writer.endCDATA();
+            // _writer.endCDATA();
+            _writer.flush();
+            _writer.close();
+            assertTrue(CORR_OUTPUT, _contentCollector.toString().equals("<![CDATA[testing]]>"));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+    public void testIllegalNestingResolvementTest() {
+        _writer = createTestProbe();
+        try {
+            _writer.startCDATA();
+            _writer.startCDATA();
+            _writer.write("testing");
+            _writer.endCDATA();
+            _writer.endCDATA();
+            _writer.flush();
+            _writer.close();
+            assertTrue(CORR_OUTPUT+ _contentCollector.toString(), _contentCollector.toString().equals("<![CDATA[<![CDATA[testing]]><![CDATA[]]]]><![CDATA[>]]>"));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+    public void testIllegalNestingResolvementTest2() {
+        _writer = createTestProbe();
+        try {
+            _writer.startCDATA();
+            _writer.startCDATA();
+            _writer.write("testing");
+            _writer.flush();
+            _writer.close();
+            assertTrue(CORR_OUTPUT+ _contentCollector.toString(), _contentCollector.toString().equals("<![CDATA[<![CDATA[testing]]>"));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+
+
+    public void testStandardUpdate() {
+        _writer = createTestProbe();
+        try {
+            _writer.startUpdate("blaId");
+            _writer.write("testing");
+            _writer.endUpdate();
+            assertTrue(CORR_OUTPUT, _contentCollector.toString().equals(STD_UPDATE_RESULT));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+    public void testStandardUpdateNestedCDATA() {
+        _writer = createTestProbe();
+        try {
+            _writer.startUpdate("blaId");
+            _writer.startCDATA();
+            _writer.write("testing");
+            _writer.endCDATA();
+            _writer.endUpdate();
+            assertTrue(CORR_OUTPUT+_contentCollector.toString(), _contentCollector.toString().equals("<changes><update id=\"blaId\"><![CDATA[<![CDATA[testing]]><![CDATA[]]]]><![CDATA[>]]></update>"));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+
+    public void testComponentAuthorNestingFailureTest() {
+        _writer = createTestProbe();
+        try {
+            _writer.startUpdate("blaId");
+            _writer.startCDATA();
+            _writer.startCDATA();
+            _writer.write("testing");
+            _writer.endUpdate();
+            assertTrue(CORR_OUTPUT+_contentCollector.toString(), _contentCollector.toString().equals("<changes><update id=\"blaId\"><![CDATA[<![CDATA[<![CDATA[testing]]></update>"));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+    public void testStandardInsertAfter() {
+        _writer = createTestProbe();
+        try {
+            _writer.startInsertAfter("blaId");
+            _writer.write("testing");
+            _writer.endInsert();
+            assertTrue(CORR_OUTPUT, _contentCollector.toString().equals("<changes><insert><after id=\"blaId\"><![CDATA[testing]]></after></insert>"));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+    public void testStandardInsertBefore() {
+        _writer = createTestProbe();
+        try {
+            _writer.startInsertBefore("blaId");
+            _writer.write("testing");
+            _writer.endInsert();
+            assertTrue(CORR_OUTPUT, _contentCollector.toString().equals("<changes><insert><before id=\"blaId\"><![CDATA[testing]]></before></insert>"));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+    public void testBrokenUserInput() {
+        _writer = createTestProbe();
+        try {
+            _writer.startInsertBefore("blaId");
+            _writer.startElement("input", null);
+            _writer.writeAttribute("type","text", null);
+            _writer.writeAttribute("value","]]>", null);
+            _writer.endElement("input");
+            _writer.endInsert();
+            assertTrue(CORR_OUTPUT+_contentCollector.toString(), _contentCollector.toString().contains("value=\"]]&gt;\""));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+
+
+    public void testDelete() {
+        _writer = createTestProbe();
+        try {
+            _writer.delete("blaId");
+            assertTrue(CORR_OUTPUT+_contentCollector.toString(), _contentCollector.toString().equals("<changes><delete id=\"blaId\"></delete>"));
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+
+
+    /**
+     * creates a new test probe (aka response writer)
+     *
+     * @return
+     */
+    private PartialResponseWriterImpl createTestProbe() {
+        return new PartialResponseWriterImpl(new HtmlResponseWriterImpl(_contentCollector, null, "UTF-8"));
+    }
+
+}