Author: gvanmatre Date: Mon Jul 3 14:53:02 2006 New Revision: 418860 URL: http://svn.apache.org/viewvc?rev=418860&view=rev Log: This commit contains a couple related fixes for SHALE-209 and SHALE-208 . I found that the clay parser was not handling cdata sections when used by a custom tag validator.
Added: struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/ClayTagValidator.java (with props) Removed: struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/Bundle.properties Modified: struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Node.java struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Parser.java struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/builder/chain/DefaultBuilderRule.java struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/SymbolTag.java struts/shale/trunk/shale-clay/src/main/resources/META-INF/shale-clay.tld struts/shale/trunk/shale-clay/src/main/resources/org/apache/shale/clay/Bundle.properties struts/shale/trunk/shale-clay/src/test/java/org/apache/shale/clay/parser/ParserTestCase.java Modified: struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Node.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Node.java?rev=418860&r1=418859&r2=418860&view=diff ============================================================================== --- struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Node.java (original) +++ struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Node.java Mon Jul 3 14:53:02 2006 @@ -49,6 +49,12 @@ */ private boolean isEnd = false; + + /** + * <p>This flag indicates the node is a CDATA node.</p> + */ + private boolean isCdata = false; + /** * <p>This boolean flag has a <code>true</code> value if the * node has a starting and ending node. Not all nodes will be @@ -238,6 +244,7 @@ buff.append("name=").append(name).append(" isStart=").append(isStart) .append(" isEnd=").append(isEnd).append(" isWellFormed=") .append(isWellFormed).append(" isComment=").append(isComment) + .append(" isCdata=").append(isCdata) .append("\n").append(token).append("\n").append(attributes); return buff.toString(); } @@ -260,8 +267,27 @@ public void setComment(boolean isComment) { this.isComment = isComment; } + - + /** + * <p> Returns <code>true</code> if the node is + * a CDATA; otherwise; the default is <code>false</code>. + * </p>. + */ + public boolean isCdata() { + return isCdata; + } + + + /** + * <p>Sets a boolean value that identifies this node as + * being a CDATA. This could be a starting, ending or + * within the body.</p> + */ + public void setCdata(boolean isCdata) { + this.isCdata = isCdata; + } + /** * <p>Finds matching nodes by <code>name</code> searching thru all the children.</p> */ Modified: struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Parser.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Parser.java?rev=418860&r1=418859&r2=418860&view=diff ============================================================================== --- struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Parser.java (original) +++ struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Parser.java Mon Jul 3 14:53:02 2006 @@ -258,7 +258,11 @@ //play forward on comments making all nodes child nodes until a //ending comment is hit - if (node.isComment() && node.isStart()) { + if ((node.isComment() || node.isCdata()) && node.isStart()) { + + // capture the type of block since you can have comments in a cdata block + boolean isCommentBlock = node.isComment(); + boolean isCdataBlock = node.isCdata(); //not self contained comment if (!node.isEnd()) { @@ -266,25 +270,29 @@ trash: while (i.hasNext()) { token = (Token) i.next(); Node bodyNode = buildNode(token); - if (bodyNode.isComment() && bodyNode.isEnd()) { + //if a ending node and the block matches + if (((bodyNode.isComment() && isCommentBlock) + || (bodyNode.isCdata() && isCdataBlock)) && bodyNode.isEnd()) { node.addChild(bodyNode); node.setEnd(true); node.setWellFormed(true); break trash; } else { - //force all nodes to be comment within a comment - node.setComment(true); + //force all nodes to be comment or cdata within a block + node.setComment(isCommentBlock); + node.setCdata(isCdataBlock); node.setWellFormed(true); node.addChild(bodyNode); } } // end while - } + } current.addChild(node); continue next; - } // end is comment + } + if (!node.isStart() && node.isEnd()) { @@ -357,7 +365,7 @@ new Object[] {node.getToken(), node.getToken().getRawText()})); } - if (!node.isComment()) { + if (!node.isComment() && !node.isCdata()) { Iterator ci = node.getChildren().iterator(); while (ci.hasNext()) { simpleWellFormedCheck((Node)(ci.next())); @@ -465,6 +473,44 @@ new Rule('>', false, -1, true), new Rule('-', false, -2, true), new Rule('-', false, -3, true)}; + + + /** + * <p>Declare an array of [EMAIL PROTECTED] Parser.Rule}s that validate self contained CDATA [EMAIL PROTECTED] Token}.</p> + */ + private static Rule[] SELF_CONTAINED_CDATA_RULES = {new Rule('<', true, 0, true), + new Rule('!', true, 1, true), + new Rule('[', true, 2, true), + new Rule('C', true, 3, true), + new Rule('D', true, 4, true), + new Rule('A', true, 5, true), + new Rule('T', true, 6, true), + new Rule('A', true, 7, true), + new Rule('[', true, 8, true), + new Rule('>', false, -1, true), + new Rule(']', false, -2, true), + new Rule(']', false, -3, true)}; + + /** + * <p>Declare an array of [EMAIL PROTECTED] Parser.Rule}s that validate a begin CDATA [EMAIL PROTECTED] Token}.</p> + */ + public static Rule[] BEGIN_CDATA_RULES = {new Rule('<', true, 0, true), + new Rule('!', true, 1, true), + new Rule('[', true, 2, true), + new Rule('C', true, 3, true), + new Rule('D', true, 4, true), + new Rule('A', true, 5, true), + new Rule('T', true, 6, true), + new Rule('A', true, 7, true), + new Rule('[', true, 8, true)}; + + /** + * <p>Declare an array of [EMAIL PROTECTED] Parser.Rule}s that validate an end CDATA [EMAIL PROTECTED] Token}.</p> + */ + public static Rule[] END_CDATA_RULES = {new Rule('>', false, -1, true), + new Rule(']', false, -2, true), + new Rule(']', false, -3, true)}; + /** * <p>Declare an array of [EMAIL PROTECTED] Parser.Rule}s that validate a begin comment [EMAIL PROTECTED] Token}.</p> @@ -513,14 +559,17 @@ * that are used to determine the type of [EMAIL PROTECTED] Node} the [EMAIL PROTECTED] Token} defines.</p> */ private static Shape[] NODE_SHAPES = { - new Shape(false, true, false, END_TAG_RULES), - new Shape(true, true, false, SELF_TERM_TAG_RULES), - new Shape(true, true, true, SELF_CONTAINED_COMMENT_RULES), - new Shape(true, false, true, BEGIN_COMMENT_TAG_RULES), - new Shape(false, true, true, END_COMMENT_TAG_RULES), - new Shape(true, true, true, DOCTYPE_TAG_RULES), - new Shape(true, false, false, BEGIN_TAG_RULES), - new Shape(true, true, true, JSP_RULES)}; + new Shape(true, true, false, true, SELF_CONTAINED_CDATA_RULES), + new Shape(true, false, false, true, BEGIN_CDATA_RULES), + new Shape(false, true, false, true, END_CDATA_RULES), + new Shape(false, true, false, false, END_TAG_RULES), + new Shape(true, true, false, false, SELF_TERM_TAG_RULES), + new Shape(true, true, true, false, SELF_CONTAINED_COMMENT_RULES), + new Shape(true, false, true, false, BEGIN_COMMENT_TAG_RULES), + new Shape(false, true, true, false, END_COMMENT_TAG_RULES), + new Shape(true, true, true, false, DOCTYPE_TAG_RULES), + new Shape(true, false, false, false, BEGIN_TAG_RULES), + new Shape(true, true, true, false, JSP_RULES)}; /** @@ -572,6 +621,7 @@ node.setStart(shape.isStart()); node.setEnd(shape.isEnd()); node.setComment(shape.isComment()); + node.setCdata(shape.isCdata); break nextShape; } @@ -592,6 +642,10 @@ node.setName("--"); + } else if (node.isCdata()) { + + node.setName("[CDATA["); + } else { // find the node name delimiter //int e = token.getDocument().indexOf(" ", token.getBeginOffset() + 2); @@ -640,7 +694,7 @@ node.setAttributes(attributes); // look for attribute in a beginning tag only - if (node.isStart() && !node.isComment()) { + if (node.isStart() && (!node.isComment() && !node.isCdata())) { int e = (node.isStart() && node.isEnd()) ? (token.getEndOffset() - 2) : (token.getEndOffset() - 1); @@ -780,6 +834,13 @@ * <p>If <code>true</code> it indicates a comment node.</p> */ private boolean isComment = false; + + + /** + * <p>If <code>true</code> it indicates a CDATA node.</p> + */ + private boolean isCdata = false; + /** * <p>An array of [EMAIL PROTECTED] Parser.Rule}s used to determine if the @@ -790,10 +851,11 @@ /** * <p>Overloaded constructor used to instantiate the immutable object.</p> */ - public Shape(boolean isStart, boolean isEnd, boolean isComment, Rule[] rules) { + public Shape(boolean isStart, boolean isEnd, boolean isComment, boolean isCdata, Rule[] rules) { this.isStart = isStart; this.isEnd = isEnd; this.isComment = isComment; + this.isCdata = isCdata; this.rules = rules; } @@ -815,6 +877,13 @@ public boolean isComment() { return isComment; } + /** + * <p>Returns <code>true</code> if the [EMAIL PROTECTED] Token} is a CDATA tag.</p> + */ + public boolean isCdata() { + return isCdata; + } + /** * <p>Returns the [EMAIL PROTECTED] Parser.Rule}s that define the <code>isStart</code>, * <code>isEnd</code> and <code>isComment</code> characteristics.</p> Modified: struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/builder/chain/DefaultBuilderRule.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/builder/chain/DefaultBuilderRule.java?rev=418860&r1=418859&r2=418860&view=diff ============================================================================== --- struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/builder/chain/DefaultBuilderRule.java (original) +++ struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/builder/chain/DefaultBuilderRule.java Mon Jul 3 14:53:02 2006 @@ -63,7 +63,7 @@ BuilderRuleContext builderRuleContext = (BuilderRuleContext) context; Node node = builderRuleContext.getNode(); - if (node.isComment()) { + if (node.isComment() || node.isCdata()) { builderRuleContext.setBuilder(builders[0]); } else if (node.getName() != null && node.getAttributes().containsKey("jsfid")) { builderRuleContext.setBuilder(builders[1]); Added: struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/ClayTagValidator.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/ClayTagValidator.java?rev=418860&view=auto ============================================================================== --- struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/ClayTagValidator.java (added) +++ struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/ClayTagValidator.java Mon Jul 3 14:53:02 2006 @@ -0,0 +1,164 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Id$ + */ + +package org.apache.shale.clay.taglib; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.jsp.tagext.PageData; +import javax.servlet.jsp.tagext.TagLibraryValidator; +import javax.servlet.jsp.tagext.ValidationMessage; + +import org.apache.shale.clay.parser.Node; +import org.apache.shale.clay.parser.Parser; +import org.apache.shale.util.Messages; + +/** + * <p>Validates the JSP page for the clay namespace, + * "http://struts.apache.org/shale/clay-plugin". This tag + * validator checks to make sure that there are not any nested + * tags under the [EMAIL PROTECTED] ClayTag} with the exception of the + * [EMAIL PROTECTED] SymbolTag}.</p> + */ +public class ClayTagValidator extends TagLibraryValidator { + + private static final String CLAY_URI_NAMESPACE = "http://struts.apache.org/shale/clay-plugin"; + + /** + * <p>Message resources for this class.</p> + */ + private static Messages messages = new Messages("org.apache.shale.clay.Bundle", + ClayTagValidator.class.getClassLoader()); + + /** + * <p>Loads the <code>page</code> content into a <code>StringBuffer</code>.</p> + */ + protected StringBuffer loadTemplate(PageData page) throws IOException { + + + StringBuffer buff = new StringBuffer(); + InputStream inputStream = page.getInputStream(); + + int c = 0; + done: while (true) { + c = inputStream.read(); + if (c > -1) + buff.append((char) c); + else + break done; + + } + + return buff; + + } + + /** + * <p>Creates a <code>ValidationMessage</code> for a [EMAIL PROTECTED] ClayTag} containing + * a child of anything other than the [EMAIL PROTECTED] SymbolTag}.</p> + */ + private ValidationMessage getMessage(String prefix, Node clayNode, Node childNode) { + Object[] args = {clayNode.getToken().getRawText(), + childNode.getToken().getRawText(), + prefix}; + String jspid = (String) childNode.getAttributes().get("jsp:id"); + String message = messages.getMessage("invalid.nested.tag", args); + return new ValidationMessage(jspid, message); + } + + + /** + * <p>Checks the child nodes of the <code>clayNode</code> verifying that + * only the symbol node is present.</p> + */ + private void checkForInvalidNestedTags(String prefix, Node clayNode, List messages) { + List children = clayNode.getChildren(); + next: for (int i = 0; i < children.size(); i++) { + Node child = (Node) children.get(i); + if ((!child.isComment() && !child.isCdata()) && child.isWellFormed()) { + if (child.getQname() != null && child.getName() != null) { + + if (child.getQname().equals("jsp") && child.getName().equals("text")) { + continue next; + } else if (!child.getName().equals("symbol") || !prefix.equals(child.getQname())) { + messages.add(getMessage(prefix, clayNode, child)); + } + } + + } + } + } + + /** + * <p>Recursively walks the parsed document looking for clay component nodes. The children + * are checked to make sure the symbol tag is the only valid child tag.</p> + */ + private void validateClayTags(String prefix, Node node, List messages) { + if ((!node.isComment() && !node.isCdata()) && node.isWellFormed() + && node.getName() != null && node.getName().equals("clay") + && node.getQname() != null && node.getQname().equals(prefix)) { + + checkForInvalidNestedTags(prefix, node, messages); + return; + } + + List children = node.getChildren(); + for (int i = 0; i < children.size(); i++) { + Node child = (Node) children.get(i); + validateClayTags(prefix, child, messages); + } + + } + + /** + * <p>Validates the page for a directive with a uri of + * "<strong>http://struts.apache.org/shale/clay-plugin</strong>". + */ + public ValidationMessage[] validate(String prefix, String uri, PageData page) { + List messages = new ArrayList(); + + if (uri != null && CLAY_URI_NAMESPACE.equals(uri)) { + try { + StringBuffer buff = loadTemplate(page); + Parser p = new Parser(); + List roots = p.parse(buff); + + for (int i = 0; i < roots.size(); i++) { + Node node = (Node) roots.get(i); + validateClayTags(prefix, node, messages); + } + + } catch (IOException e) { + messages.add(new ValidationMessage(null, e.getMessage())); + } + } + + if (messages.isEmpty()) + return null; + else { + ValidationMessage[] validationMessages = new ValidationMessage[messages.size()]; + messages.toArray(validationMessages); + return validationMessages; + } + + } + +} Propchange: struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/ClayTagValidator.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/ClayTagValidator.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Modified: struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/SymbolTag.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/SymbolTag.java?rev=418860&r1=418859&r2=418860&view=diff ============================================================================== --- struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/SymbolTag.java (original) +++ struts/shale/trunk/shale-clay/src/main/java/org/apache/shale/clay/taglib/SymbolTag.java Mon Jul 3 14:53:02 2006 @@ -12,6 +12,8 @@ * 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. + * + * $Id$ */ package org.apache.shale.clay.taglib; Modified: struts/shale/trunk/shale-clay/src/main/resources/META-INF/shale-clay.tld URL: http://svn.apache.org/viewvc/struts/shale/trunk/shale-clay/src/main/resources/META-INF/shale-clay.tld?rev=418860&r1=418859&r2=418860&view=diff ============================================================================== --- struts/shale/trunk/shale-clay/src/main/resources/META-INF/shale-clay.tld (original) +++ struts/shale/trunk/shale-clay/src/main/resources/META-INF/shale-clay.tld Mon Jul 3 14:53:02 2006 @@ -27,6 +27,12 @@ <uri>http://struts.apache.org/shale/clay-plugin</uri> <description></description> + <validator> + <validator-class> + org.apache.shale.clay.taglib.ClayTagValidator + </validator-class> + </validator> + <listener> <listener-class>org.apache.shale.clay.config.ClayConfigureListener</listener-class> </listener> Modified: struts/shale/trunk/shale-clay/src/main/resources/org/apache/shale/clay/Bundle.properties URL: http://svn.apache.org/viewvc/struts/shale/trunk/shale-clay/src/main/resources/org/apache/shale/clay/Bundle.properties?rev=418860&r1=418859&r2=418860&view=diff ============================================================================== --- struts/shale/trunk/shale-clay/src/main/resources/org/apache/shale/clay/Bundle.properties (original) +++ struts/shale/trunk/shale-clay/src/main/resources/org/apache/shale/clay/Bundle.properties Mon Jul 3 14:53:02 2006 @@ -40,6 +40,9 @@ attributes.total.found=Found {0} attribute(s). attribute.token.range=Attribute token offset range({0} - {1}). +#org.apache.shale.clay.taglib.ClayTagValidator +invalid.nested.tag=Tag "{0}" contains an invalid nested tag "{1}"\n Only the "{2}:symbol" tag is the only valid nested tag under the clay component. + #org.apache.shale.clay.component.Clay clay.jsfid.notfound=Component not found "{0}". clay.jsfid.null=The Component jsfid attribute is null. The cause is most likely a\nmissing "clayJsfid" attribute when using HTML templates and nesting \na clay component. It is also likely that the problem was caused by using \ngeneric templates with symbol replacement of the clayJsfid={0}. Modified: struts/shale/trunk/shale-clay/src/test/java/org/apache/shale/clay/parser/ParserTestCase.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/shale-clay/src/test/java/org/apache/shale/clay/parser/ParserTestCase.java?rev=418860&r1=418859&r2=418860&view=diff ============================================================================== --- struts/shale/trunk/shale-clay/src/test/java/org/apache/shale/clay/parser/ParserTestCase.java (original) +++ struts/shale/trunk/shale-clay/src/test/java/org/apache/shale/clay/parser/ParserTestCase.java Mon Jul 3 14:53:02 2006 @@ -749,7 +749,33 @@ // default namespace uri = clayComponent.getNamespaceURI(null); assertEquals("uri", "http://www.w3.org/1999/xhtml", uri); - + + } + + + public void testParseCDATA() { + Parser p = new Parser(); + StringBuffer doc = new StringBuffer(); + doc.append("<jsp:text\njsp:id=\"9\"\n><![CDATA[\n<html>\n]]>\n</jsp:text>"); + doc.append("<jsp:text\njsp:id=\"10\"\n><![CDATA[\n<head>\n]]>\n</jsp:text>"); + doc.append("<jsp:text\njsp:id=\"11\"\n><![CDATA[\n</title>\n]]>\n</jsp:text>"); + doc.append("<jsp:text\njsp:id=\"12\"\n><![CDATA[\n<!-- </title> -->\n]]>\n</jsp:text>"); + + List roots = p.parse(doc); + assertEquals(4, roots.size()); + + for (int i = 0; i < roots.size(); i++) { + Node node = (Node) roots.get(i); + assertEquals("jsp", node.getQname()); + assertEquals("text", node.getName()); + + String id = (String) node.getAttributes().get("jsp:id"); + assertEquals(String.valueOf(i + 9), id); + + + + } + }