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 ]]> into &MYFACES_MAPPED_ENDCDATA; on the server side
+ * and then remap it back into ]]> 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
+ * ]]> becomes <![CDATA[]]]]><![CDATA[>
+ * <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=\"]]>\""));
+ } 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"));
+ }
+
+}