--- impl/src/test/java/org/apache/taglibs/standard/functions/TestFunctions.java (revision 961246) +++ impl/src/test/java/org/apache/taglibs/standard/functions/TestFunctions.java (working copy) @@ -111,4 +111,12 @@ Assert.assertEquals(Resources.getMessage("PARAM_BAD_VALUE"), e.getMessage()); } } + + @Test + public void testEscapeXML() { + Assert.assertEquals("Hello", escapeXml("Hello")); + Assert.assertEquals("<Hello msg="world"/>", escapeXml("")); + Assert.assertEquals("<Hello msg='world'/>", escapeXml("")); + Assert.assertEquals("cats & dogs", escapeXml("cats & dogs")); + } } --- impl/src/test/java/org/apache/taglibs/standard/util/EscapeXMLTest.java (revision 0) +++ impl/src/test/java/org/apache/taglibs/standard/util/EscapeXMLTest.java (revision 0) @@ -0,0 +1,220 @@ +/* + * 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.taglibs.standard.util; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import javax.servlet.jsp.JspWriter; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +import static org.apache.taglibs.standard.util.EscapeXML.*; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.eq; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.isA; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; + +/** + */ +public class EscapeXMLTest { + private JspWriter writer; + + @Before + public void setup() { + writer = createMock(JspWriter.class); + } + + @Test + public void testEscapeString() { + Assert.assertEquals("Hello", escape("Hello")); + Assert.assertEquals("<Hello msg="world"/>", escape("")); + Assert.assertEquals("<Hello msg='world'/>", escape("")); + Assert.assertEquals("cats & dogs", escape("cats & dogs")); + } + + @Test + public void testEmitInteger() throws IOException { + writer.write("1234"); + replay(writer); + emit(1234, false, writer); + verify(writer); + } + + @Test + public void testEmitNull() throws IOException { + writer.write("null"); + replay(writer); + emit((Object) null, false, writer); + verify(writer); + } + + @Test + public void testEmitStringEscaped() throws IOException { + String s = "cats & dogs"; + writer.write(s, 0, 5); + writer.write("&"); + writer.write(s, 6, 5); + replay(writer); + emit(s, true, writer); + verify(writer); + } + + @Test + public void testEmitStringUnescaped() throws IOException { + String s = "cats & dogs"; + writer.write(s); + replay(writer); + emit(s, false, writer); + verify(writer); + } + + @Test + public void testEmitEntireString() throws IOException { + writer.write("Hello World", 0, 11); + replay(writer); + emit("Hello World", writer); + verify(writer); + } + + @Test + public void testEmitEscapedStringMiddle() throws IOException { + String s = "cats & dogs"; + writer.write(s, 0, 5); + writer.write("&"); + writer.write(s, 6, 5); + replay(writer); + emit(s, writer); + verify(writer); + } + + @Test + public void testEmitEscapedStringBeginning() throws IOException { + String s = "& dogs"; + writer.write("&"); + writer.write(s, 1, 5); + replay(writer); + emit(s, writer); + verify(writer); + } + + @Test + public void testEmitEscapedStringEnd() throws IOException { + String s = "cats &"; + writer.write(s, 0, 5); + writer.write("&"); + replay(writer); + emit(s, writer); + verify(writer); + } + + @Test + public void testEmitEscapedStringAdjacent() throws IOException { + String s = "'cats'&\"dogs\""; + writer.write("'"); + writer.write(s, 1, 4); + writer.write("'"); + writer.write("&"); + writer.write("""); + writer.write(s, 8, 4); + writer.write("""); + replay(writer); + emit(s, writer); + verify(writer); + } + + @Test + public void testEmitEmptyString() throws IOException { + replay(writer); + emit("", writer); + verify(writer); + } + + @Test + public void testEmitChars() throws IOException { + char[] chars = "Hello World".toCharArray(); + writer.write(chars, 2, 5); + replay(writer); + emit(chars, 2, 5, writer); + verify(writer); + } + + @Test + public void testEmitEscapedChars() throws IOException { + char[] chars = "'cats'&\"dogs\"".toCharArray(); + writer.write("'"); + writer.write(chars, 1, 4); + writer.write("'"); + writer.write("&"); + writer.write("""); + writer.write(chars, 8, 4); + writer.write("""); + replay(writer); + emit(chars, 0, chars.length, writer); + verify(writer); + } + + @Test + public void testEmitReaderUnescaped() throws IOException { + Reader reader = new StringReader("'cats'&\"dogs\""); + expect(writer.getBufferSize()).andStubReturn(0); + writer.write(isA(char[].class), eq(0), eq(13)); + replay(writer); + emit(reader, false, writer); + verify(writer); + } + + @Test + public void testEmitReaderUnbuffered() throws IOException { + Reader reader = new StringReader("'cats'&\"dogs\""); + expect(writer.getBufferSize()).andStubReturn(0); + writer.write("'"); + writer.write(isA(char[].class), eq(1), eq(4)); + writer.write("'"); + writer.write("&"); + writer.write("""); + writer.write(isA(char[].class), eq(8), eq(4)); + writer.write("""); + replay(writer); + emit(reader, true, writer); + verify(writer); + } + + @Test + public void testEmitReaderBufferWrap() throws IOException { + Reader reader = new StringReader("'cats'&\"dogs\""); + expect(writer.getBufferSize()).andStubReturn(2); + writer.write("'"); + writer.write(isA(char[].class), eq(1), eq(1)); // 'c' + writer.write(isA(char[].class), eq(0), eq(2)); // 'at' + writer.write(isA(char[].class), eq(0), eq(1)); // 's' + writer.write("'"); + writer.write("&"); + writer.write("""); + writer.write(isA(char[].class), eq(0), eq(2)); // 'do' + writer.write(isA(char[].class), eq(0), eq(2)); // 'gs' + writer.write("""); + replay(writer); + emit(reader, true, writer); + verify(writer); + } +} --- impl/src/main/java/org/apache/taglibs/standard/functions/Functions.java (revision 961246) +++ impl/src/main/java/org/apache/taglibs/standard/functions/Functions.java (working copy) @@ -27,7 +27,7 @@ import javax.servlet.jsp.JspTagException; import org.apache.taglibs.standard.resources.Resources; -import org.apache.taglibs.standard.tag.common.core.Util; +import org.apache.taglibs.standard.util.EscapeXML; /** * Static functions that extend the Expression Language with standardized behaviour @@ -207,7 +207,7 @@ * @return escaped string */ public static String escapeXml(String input) { - return Util.escapeXml(input); + return EscapeXML.escape(input); } /** --- impl/src/main/java/org/apache/taglibs/standard/tag/common/xml/ExprSupport.java (revision 961246) +++ impl/src/main/java/org/apache/taglibs/standard/tag/common/xml/ExprSupport.java (working copy) @@ -17,13 +17,17 @@ package org.apache.taglibs.standard.tag.common.xml; +import org.apache.taglibs.standard.util.EscapeXML; + import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspTagException; import javax.servlet.jsp.tagext.TagSupport; /** - *

Tag handler for <expr> in JSTL's XML library.

+ *

Tag handler for <out> in JSTL's XML library.

* + * TODO: should we rename this to OutSupport to match the tag name? + * * @author Shawn Bayern */ @@ -61,13 +65,12 @@ // applies XPath expression from 'select' and prints the result public int doStartTag() throws JspException { try { - XPathUtil xu = new XPathUtil(pageContext); - String result = xu.valueOf(XPathUtil.getContext(this), select); - org.apache.taglibs.standard.tag.common.core.OutSupport.out( - pageContext, escapeXml, result); - return SKIP_BODY; + XPathUtil xu = new XPathUtil(pageContext); + String result = xu.valueOf(XPathUtil.getContext(this), select); + EscapeXML.emit(result, escapeXml, pageContext.getOut()); + return SKIP_BODY; } catch (java.io.IOException ex) { - throw new JspTagException(ex.toString(), ex); + throw new JspTagException(ex.toString(), ex); } } --- impl/src/main/java/org/apache/taglibs/standard/tag/common/core/OutSupport.java (revision 961246) +++ impl/src/main/java/org/apache/taglibs/standard/tag/common/core/OutSupport.java (working copy) @@ -17,12 +17,12 @@ package org.apache.taglibs.standard.tag.common.core; +import org.apache.taglibs.standard.util.EscapeXML; + import java.io.IOException; -import java.io.Reader; import javax.servlet.jsp.JspException; -import javax.servlet.jsp.JspWriter; -import javax.servlet.jsp.PageContext; +import javax.servlet.jsp.JspTagException; import javax.servlet.jsp.tagext.BodyTagSupport; /** @@ -47,7 +47,7 @@ protected Object value; // tag attribute protected String def; // tag attribute protected boolean escapeXml; // tag attribute - private boolean needBody; // non-space body needed? + private Object output; //********************************************************************* // Construction and initialization @@ -65,8 +65,8 @@ // resets local state private void init() { value = def = null; + output = null; escapeXml = true; - needBody = false; } // Releases any resources we may have (or inherit) @@ -79,127 +79,42 @@ //********************************************************************* // Tag logic - // evaluates 'value' and determines if the body should be evaluted + @Override public int doStartTag() throws JspException { - needBody = false; // reset state related to 'default' - this.bodyContent = null; // clean-up body (just in case container is pooling tag handlers) - - try { - // print value if available; otherwise, try 'default' - if (value != null) { - out(pageContext, escapeXml, value); - return SKIP_BODY; - } else { - // if we don't have a 'default' attribute, just go to the body - if (def == null) { - needBody = true; - return EVAL_BODY_BUFFERED; - } + this.bodyContent = null; // clean-up body (just in case container is pooling tag handlers) - // if we do have 'default', print it - if (def != null) { - // good 'default' - out(pageContext, escapeXml, def); - } - return SKIP_BODY; - } - } catch (IOException ex) { - throw new JspException(ex.toString(), ex); - } - } + // output value if not null + if (value != null) { + output = value; + return SKIP_BODY; + } - // prints the body if necessary; reports errors - public int doEndTag() throws JspException { - try { - if (!needBody) - return EVAL_PAGE; // nothing more to do + // output default if supplied + if (def != null ) { + output = def; + return SKIP_BODY; + } - // trim and print out the body - if (bodyContent != null && bodyContent.getString() != null) - out(pageContext, escapeXml, bodyContent.getString().trim()); - return EVAL_PAGE; - } catch (IOException ex) { - throw new JspException(ex.toString(), ex); - } + // output body as default + output = ""; // need to reset as doAfterBody will not be called with an empty tag + // TODO: to avoid buffering, can we wrap out in a filter that performs escaping and use EVAL_BODY_INCLUDE? + return EVAL_BODY_BUFFERED; } - - //********************************************************************* - // Public utility methods - - /** - * Outputs text to pageContext's current JspWriter. - * If escapeXml is true, performs the following substring - * replacements (to facilitate output to XML/HTML pages): - * - * & -> & - * < -> < - * > -> > - * " -> " - * ' -> ' - * - * See also Util.escapeXml(). - */ - public static void out(PageContext pageContext, - boolean escapeXml, - Object obj) throws IOException { - JspWriter w = pageContext.getOut(); - if (!escapeXml) { - // write chars as is - if (obj instanceof Reader) { - Reader reader = (Reader)obj; - char[] buf = new char[4096]; - int count; - while ((count=reader.read(buf, 0, 4096)) != -1) { - w.write(buf, 0, count); - } - } else { - w.write(obj.toString()); - } - } else { - // escape XML chars - if (obj instanceof Reader) { - Reader reader = (Reader)obj; - char[] buf = new char[4096]; - int count; - while ((count = reader.read(buf, 0, 4096)) != -1) { - writeEscapedXml(buf, count, w); - } - } else { - String text = obj.toString(); - writeEscapedXml(text.toCharArray(), text.length(), w); - } - } + @Override + public int doAfterBody() throws JspException { + output = bodyContent.getString().trim(); + return SKIP_BODY; } - /** - * - * Optimized to create no extra objects and write directly - * to the JspWriter using blocks of escaped and unescaped characters - * - */ - private static void writeEscapedXml(char[] buffer, int length, JspWriter w) throws IOException{ - int start = 0; - - for (int i = 0; i < length; i++) { - char c = buffer[i]; - if (c <= Util.HIGHEST_SPECIAL) { - char[] escaped = Util.specialCharactersRepresentation[c]; - if (escaped != null) { - // add unescaped portion - if (start < i) { - w.write(buffer,start,i-start); - } - // add escaped xml - w.write(escaped); - start = i + 1; - } - } + @Override + public int doEndTag() throws JspException { + try { + EscapeXML.emit(output, escapeXml, pageContext.getOut()); + } catch (IOException e) { + throw new JspTagException(e); } - // add rest of unescaped portion - if (start < length) { - w.write(buffer,start,length-start); - } + return EVAL_PAGE; } } --- impl/src/main/java/org/apache/taglibs/standard/tag/common/core/Util.java (revision 961246) +++ impl/src/main/java/org/apache/taglibs/standard/tag/common/core/Util.java (working copy) @@ -17,9 +17,6 @@ package org.apache.taglibs.standard.tag.common.core; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; import java.text.DateFormat; import java.util.Enumeration; import java.util.Vector; @@ -46,16 +43,6 @@ private static final String LONG = "long"; private static final String FULL = "full"; - public static final int HIGHEST_SPECIAL = '>'; - public static char[][] specialCharactersRepresentation = new char[HIGHEST_SPECIAL + 1][]; - static { - specialCharactersRepresentation['&'] = "&".toCharArray(); - specialCharactersRepresentation['<'] = "<".toCharArray(); - specialCharactersRepresentation['>'] = ">".toCharArray(); - specialCharactersRepresentation['"'] = """.toCharArray(); - specialCharactersRepresentation['\''] = "'".toCharArray(); - } - /* * Converts the given string description of a scope to the corresponding * PageContext constant. @@ -113,58 +100,9 @@ return ret; } - /** - * Performs the following substring replacements - * (to facilitate output to XML/HTML pages): - * - * & -> & - * < -> < - * > -> > - * " -> " - * ' -> ' - * - * See also OutSupport.writeEscapedXml(). - */ - public static String escapeXml(String buffer) { - int start = 0; - int length = buffer.length(); - char[] arrayBuffer = buffer.toCharArray(); - StringBuffer escapedBuffer = null; - - for (int i = 0; i < length; i++) { - char c = arrayBuffer[i]; - if (c <= HIGHEST_SPECIAL) { - char[] escaped = specialCharactersRepresentation[c]; - if (escaped != null) { - // create StringBuffer to hold escaped xml string - if (start == 0) { - escapedBuffer = new StringBuffer(length + 5); - } - // add unescaped portion - if (start < i) { - escapedBuffer.append(arrayBuffer,start,i-start); - } - start = i + 1; - // add escaped xml - escapedBuffer.append(escaped); - } - } - } - // no xml escaping was necessary - if (start == 0) { - return buffer; - } - // add rest of unescaped portion - if (start < length) { - escapedBuffer.append(arrayBuffer,start,length-start); - } - return escapedBuffer.toString(); - } - - /** * Get the value associated with a content-type attribute. * Syntax defined in RFC 2045, section 5.1. */ --- impl/src/main/java/org/apache/taglibs/standard/util/EscapeXML.java (revision 0) +++ impl/src/main/java/org/apache/taglibs/standard/util/EscapeXML.java (revision 0) @@ -0,0 +1,205 @@ +/* + * 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.taglibs.standard.util; + +import javax.servlet.jsp.JspWriter; +import java.io.IOException; +import java.io.Reader; + +/** + * Handles escaping of characters that could be interpreted as XML markup. + * + * The specification for <c:out> defines the following + * character conversions to be applied: + * + * + * + * + * + * + * + * + * + * + * + * + *
CharacterCharacter Entity Code
<&lt;
>&gt;
&&amp;
'&#039;
"&#034;
+ */ +public class EscapeXML { + + private static final String[] ESCAPES; + static { + int size = '>' + 1; // '>' is the largest escaped value + ESCAPES = new String[size]; + ESCAPES['<'] = "<"; + ESCAPES['>'] = ">"; + ESCAPES['&'] = "&"; + ESCAPES['\''] = "'"; + ESCAPES['"'] = """; + } + + private static String getEscape(char c) { + if (c < ESCAPES.length) { + return ESCAPES[c]; + } else { + return null; + } + } + + /** + * Escape a string. + * + * @param src the string to escape; must not be null + * @return the escaped string + */ + public static String escape(String src) { + // first pass to determine the length of the buffer so we only allocate once + int length = 0; + for (int i = 0; i < src.length(); i++) { + char c = src.charAt(i); + String escape = getEscape(c); + if (escape != null) { + length += escape.length(); + } else { + length += 1; + } + } + + // skip copy if no escaping is needed + if (length == src.length()) { + return src; + } + + // second pass to build the escaped string + StringBuilder buf = new StringBuilder(length); + for (int i = 0; i < src.length(); i++) { + char c = src.charAt(i); + String escape = getEscape(c); + if (escape != null) { + buf.append(escape); + } else { + buf.append(c); + } + } + return buf.toString(); + } + + /** + * Emit the supplied object to the specified writer, escaping characters if needed. + * @param src the object to write + * @param escapeXml if true, escape unsafe characters before writing + * @param out the JspWriter to emit to + * @throws IOException if there was a problem emitting the content + */ + public static void emit(Object src, boolean escapeXml, JspWriter out) throws IOException { + if (src instanceof Reader) { + emit((Reader) src, escapeXml, out); + } else { + emit(String.valueOf(src), escapeXml, out); + } + } + + /** + * Emit the supplied String to the specified writer, escaping characters if needed. + * @param src the String to write + * @param escapeXml if true, escape unsafe characters before writing + * @param out the JspWriter to emit to + * @throws IOException if there was a problem emitting the content + */ + public static void emit(String src, boolean escapeXml, JspWriter out) throws IOException { + if (escapeXml) { + emit(src, out); + } else { + out.write(src); + } + } + + /** + * Emit escaped content into the specified JSPWriter. + * + * @param src the string to escape; must not be null + * @param out the JspWriter to emit to + * @throws IOException if there was a problem emitting the content + */ + public static void emit(String src, JspWriter out) throws IOException { + int end = src.length(); + int from = 0; + for (int to = from ; to < end; to++) { + String escape = getEscape(src.charAt(to)); + if (escape != null) { + if (to != from) { + out.write(src, from, to-from); + } + out.write(escape); + from = to + 1; + } + } + if (from != end) { + out.write(src, from, end-from); + } + } + + /** + * Copy the content of a Reader into the specified JSPWriter escaping characters if needed. + * + * @param src the Reader to read from + * @param escapeXml if true, escape characters + * @param out the JspWriter to emit to + * @throws IOException if there was a problem emitting the content + */ + public static void emit(Reader src, boolean escapeXml, JspWriter out) throws IOException { + int bufferSize = out.getBufferSize(); + if (bufferSize == 0) { + bufferSize = 4096; + } + char[] buffer = new char[bufferSize]; + int count; + while ((count = src.read(buffer)) > 0) { + if (escapeXml) { + emit(buffer, 0, count, out); + } else { + out.write(buffer, 0, count); + } + } + } + + /** + * Emit escaped content into the specified JSPWriter. + * + * @param buffer characters to escape + * @param from start position in the buffer + * @param count number of characters to emit + * @param out the JspWriter to emit to + * @throws IOException if there was a problem emitting the content + */ + public static void emit(char[] buffer, int from, int count, JspWriter out) throws IOException { + int end = from + count; + for (int to = from ; to < end; to++) { + String escape = getEscape(buffer[to]); + if (escape != null) { + if (to != from) { + out.write(buffer, from, to-from); + } + out.write(escape); + from = to + 1; + } + } + if (from != end) { + out.write(buffer, from, end-from); + } + } +} --- standard-test/src/test/java/org/apache/taglibs/standard/tag/el/core/TestOutTag.java (revision 0) +++ standard-test/src/test/java/org/apache/taglibs/standard/tag/el/core/TestOutTag.java (revision 0) @@ -0,0 +1,40 @@ +/* + * 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.taglibs.standard.tag.el.core; + +import org.apache.cactus.ServletTestCase; +import org.apache.cactus.WebResponse; +import org.apache.taglibs.standard.testutil.TestUtil; + +import javax.servlet.RequestDispatcher; +import java.io.StringReader; + +/** + */ +public class TestOutTag extends ServletTestCase { + + public void testValue() throws Exception { + request.setAttribute("cats", new StringReader("cats & dogs")); + request.setAttribute("dogs", new StringReader("cats & dogs")); + RequestDispatcher rd = config.getServletContext().getRequestDispatcher(TestUtil.getTestJsp(this)); + rd.forward(request, response); + } + + public void endValue(WebResponse response) throws Exception { + assertEquals(TestUtil.loadResource(this), response.getText()); + } +} --- standard-test/src/test/java/org/apache/taglibs/standard/testutil/TestUtil.java (revision 961246) +++ standard-test/src/test/java/org/apache/taglibs/standard/testutil/TestUtil.java (working copy) @@ -17,6 +17,11 @@ package org.apache.taglibs.standard.testutil; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + /** * Helper class for Cactus tests */ @@ -35,4 +40,21 @@ String baseName = className.replace('.', '/'); return "/" + baseName + ".jsp"; } + + public static String loadResource(Object obj) throws IOException { + Class clazz = obj.getClass(); + InputStream is = clazz.getResourceAsStream(clazz.getSimpleName() + ".txt"); + Reader reader = new InputStreamReader(is); + try { + char[] buffer = new char[1024]; + StringBuilder s = new StringBuilder(); + int count; + while ((count = reader.read(buffer)) > 0) { + s.append(buffer, 0, count); + } + return s.toString(); + } finally { + reader.close(); + } + } } --- standard-test/src/test/resources/org/apache/taglibs/standard/tag/el/core/TestOutTag.txt (revision 0) +++ standard-test/src/test/resources/org/apache/taglibs/standard/tag/el/core/TestOutTag.txt (revision 0) @@ -0,0 +1,15 @@ + +Start +Hello World +cats & dogs +cats & dogs +5 + +default +Default from Body +<b>cats & dogs</b> +cats & dogs +Reader +cats & dogs +cats & dogs +End --- standard-test/src/main/webapp/org/apache/taglibs/standard/tag/el/core/TestOutTag.jsp (revision 0) +++ standard-test/src/main/webapp/org/apache/taglibs/standard/tag/el/core/TestOutTag.jsp (revision 0) @@ -0,0 +1,17 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +Start + + + + + + +<%-- this will be trimmed --%> + Default from Body + +cats & dogs +cats & dogs +Reader + + +End --- standard-test/pom.xml (revision 961246) +++ standard-test/pom.xml (working copy) @@ -69,8 +69,7 @@ 1.2-SNAPSHOT - - javax.servlet servlet-api @@ -89,7 +88,7 @@ 1.0 provided - --> + xalan xalan @@ -192,7 +191,7 @@ org.mortbay.jetty maven-jetty-plugin - 6.1.10 + 6.1.24 10 9999