Repository: wicket Updated Branches: refs/heads/wicket-6.x 13e5d202c -> c6f3efb72
WICKET-6278 improved support for void elements Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/c6f3efb7 Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/c6f3efb7 Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/c6f3efb7 Branch: refs/heads/wicket-6.x Commit: c6f3efb72f830088f4fa22f7d9a27a5a2b1f3136 Parents: 13e5d20 Author: Sven Meier <svenme...@apache.org> Authored: Thu Nov 17 16:53:27 2016 +0100 Committer: Sven Meier <svenme...@apache.org> Committed: Thu Nov 17 17:01:56 2016 +0100 ---------------------------------------------------------------------- .../apache/wicket/util/tester/TagTester.java | 319 +++++++------------ .../wicket/util/tester/TagTesterTest.java | 12 +- 2 files changed, 132 insertions(+), 199 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/c6f3efb7/wicket-core/src/main/java/org/apache/wicket/util/tester/TagTester.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/util/tester/TagTester.java b/wicket-core/src/main/java/org/apache/wicket/util/tester/TagTester.java index 3220cae..310530b 100644 --- a/wicket-core/src/main/java/org/apache/wicket/util/tester/TagTester.java +++ b/wicket-core/src/main/java/org/apache/wicket/util/tester/TagTester.java @@ -17,8 +17,9 @@ package org.apache.wicket.util.tester; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.Map; +import java.util.Stack; import java.util.regex.Pattern; import org.apache.wicket.WicketRuntimeException; @@ -359,6 +360,11 @@ public class TagTester */ public String getValue() { + if (openTag == closeTag) + { + return null; + } + int openPos = openTag.getPos() + openTag.getLength(); int closePos = closeTag.getPos(); @@ -370,85 +376,40 @@ public class TagTester * that it will return the first tag which matches the criteria. * * @param markup - * the markup to look for the tag to create the <code>TagTester</code> from - * the value which the attribute must have + * the markup to look for the tag to create the <code>TagTester</code> from the value + * which the attribute must have * @return the <code>TagTester</code> which matches the tag by name in the markup */ - public static TagTester createTagByAttribute(String markup, String tagName) + public static TagTester createTagByName(String markup, final String tagName) { - TagTester tester = null; - - if (Strings.isEmpty(markup) == false && Strings.isEmpty(tagName) == false) - { - try - { - // remove the CDATA and - // the id attribute of the component because it is often the same as the element's id - markup = AJAX_COMPONENT_CDATA_OPEN.matcher(markup).replaceAll("<component>"); - markup = AJAX_COMPONENT_CDATA_CLOSE.matcher(markup).replaceAll("</component>"); - - XmlPullParser parser = new XmlPullParser(); - parser.parse(markup); - - XmlTag elm; - XmlTag openTag = null; - XmlTag closeTag = null; - int level = 0; - while ((elm = parser.nextTag()) != null && closeTag == null) - { - XmlTag xmlTag = elm; - - String xmlTagName = xmlTag.getName(); - if (openTag == null && xmlTagName.equalsIgnoreCase(tagName)) - { - if (xmlTag.isOpen()) - { - openTag = xmlTag; - } - else if (xmlTag.isOpenClose()) - { - openTag = xmlTag; - closeTag = xmlTag; - } - } - else if (openTag != null) - { - String openTagName = openTag.getName(); - if (xmlTag.isOpen() && xmlTagName.equals(openTagName)) - { - level++; - } - - if (xmlTag.isClose()) - { - if (xmlTagName.equals(openTagName)) - { - if (level == 0) - { - closeTag = xmlTag; - closeTag.setOpenTag(openTag); - } - else - { - level--; - } - } - } - } - } - - if (openTag != null && closeTag != null) - { - tester = new TagTester(parser, openTag, closeTag); - } - } - catch (Exception e) - { - throw new WicketRuntimeException(e); + List<TagTester> tester = createTags(markup, new Predicate<XmlTag>() { + @Override + public boolean apply(XmlTag xmlTag) { + return xmlTag.getName().equalsIgnoreCase(tagName); } + }, true); + if ((tester == null) || (tester.size() == 0)) + { + return null; } + return tester.get(0); + } - return tester; + /** + * Static factory method for creating a <code>TagTester</code> based on a tag name. Please note + * that it will return the first tag which matches the criteria. + * + * @param markup + * the markup to look for the tag to create the <code>TagTester</code> from + * the value which the attribute must have + * @return the <code>TagTester</code> which matches the tag by name in the markup + * + * @deprecated use {@link #createTagByName(String, String)} instead + */ + @Deprecated + public static TagTester createTagByAttribute(String markup, String tagName) + { + return createTagByName(markup, tagName); } /** @@ -468,91 +429,12 @@ public class TagTester */ public static TagTester createTagByAttribute(String markup, String attribute, String value) { - TagTester tester = null; - - if (Strings.isEmpty(markup) == false && Strings.isEmpty(attribute) == false && - Strings.isEmpty(value) == false) + List<TagTester> tester = createTagsByAttribute(markup, attribute, value, true); + if ((tester == null) || (tester.size() == 0)) { - try - { - // remove the CDATA and - // the id attribute of the component because it is often the same as the element's id - markup = AJAX_COMPONENT_CDATA_OPEN.matcher(markup).replaceAll("<component>"); - markup = AJAX_COMPONENT_CDATA_CLOSE.matcher(markup).replaceAll("</component>"); - - XmlPullParser parser = new XmlPullParser(); - parser.parse(markup); - - XmlTag elm; - XmlTag openTag = null; - XmlTag closeTag = null; - int level = 0; - while ((elm = parser.nextTag()) != null && closeTag == null) - { - XmlTag xmlTag = elm; - - if (openTag == null) - { - IValueMap attributeMap = xmlTag.getAttributes(); - - for (Map.Entry<String, Object> entry : attributeMap.entrySet()) - { - String attr = entry.getKey(); - if (attr.equals(attribute) && value.equals(entry.getValue())) - { - if (xmlTag.isOpen()) - { - openTag = xmlTag; - } - else if (xmlTag.isOpenClose()) - { - openTag = xmlTag; - closeTag = xmlTag; - } - } - } - } - else - { - if (xmlTag.isOpen() && xmlTag.getName().equals(openTag.getName())) - { - level++; - } - - if (xmlTag.isClose()) - { - if (xmlTag.getName().equals(openTag.getName())) - { - if (level == 0) - { - closeTag = xmlTag; - closeTag.setOpenTag(openTag); - } - else - { - level--; - } - } - } - } - } - - if (openTag != null && closeTag != null) - { - tester = new TagTester(parser, openTag, closeTag); - } - else if (openTag != null) - { - tester = new TagTester(parser, openTag, openTag); - } - } - catch (Exception e) - { - throw new WicketRuntimeException(e); - } + return null; } - - return tester; + return tester.get(0); } /** @@ -570,14 +452,10 @@ public class TagTester * @return the <code>TagTester</code> which matches the tag in the markup, that has the given * value on the given attribute */ + @Deprecated public static TagTester createTagsByAttribute(String markup, String attribute, String value) { - List<TagTester> tester = createTagsByAttribute(markup, attribute, value, true); - if ((tester == null) || (tester.size() == 0)) - { - return null; - } - return tester.get(0); + return createTagByAttribute(markup, attribute, value); } /** @@ -597,13 +475,25 @@ public class TagTester * @return the <code>TagTester</code> which matches the tag in the markup, that has the given * value on the given attribute */ - public static List<TagTester> createTagsByAttribute(String markup, String attribute, - String value, boolean stopAfterFirst) + public static List<TagTester> createTagsByAttribute(String markup, final String attribute, + final String value, boolean stopAfterFirst) + { + if (Strings.isEmpty(attribute) || Strings.isEmpty(value)) { + return Collections.emptyList(); + } + + return createTags(markup, new Predicate<XmlTag>() { + public boolean apply(XmlTag xmlTag) { + return value.equals(xmlTag.getAttributes().get(attribute)); + } + }, stopAfterFirst); + } + + private static List<TagTester> createTags(String markup, Predicate<XmlTag> accept, boolean stopAfterFirst) { List<TagTester> testers = new ArrayList<TagTester>(); - if ((Strings.isEmpty(markup) == false) && (Strings.isEmpty(attribute) == false) && - (Strings.isEmpty(value) == false)) + if ((Strings.isEmpty(markup) == false)) { try { @@ -615,70 +505,74 @@ public class TagTester XmlPullParser parser = new XmlPullParser(); parser.parse(markup); - XmlTag elm; XmlTag openTag = null; XmlTag closeTag = null; - int level = 0; - while ((elm = parser.nextTag()) != null) + + // temporary Tag-Hierarchy after openTag + Stack<XmlTag> stack = new Stack<XmlTag>(); + + while (true) { - XmlTag xmlTag = elm; + XmlTag xmlTag = parser.nextTag(); + if (xmlTag == null) + { + break; + } + if (openTag == null) { - IValueMap attributeMap = xmlTag.getAttributes(); - for (Map.Entry<String, Object> entry : attributeMap.entrySet()) + if (accept.apply(xmlTag)) { - if (entry.getKey().equals(attribute) && value.equals(entry.getValue())) + if (xmlTag.isOpen()) { - if (xmlTag.isOpen()) - { - openTag = xmlTag; - } - else if (xmlTag.isOpenClose()) - { - openTag = xmlTag; - closeTag = xmlTag; - } + openTag = xmlTag; + } + else if (xmlTag.isOpenClose()) + { + openTag = xmlTag; + closeTag = xmlTag; } } } else { - if (xmlTag.isOpen() && xmlTag.getName().equals(openTag.getName())) + if (xmlTag.isOpen() && !xmlTag.isOpenClose()) { - level++; + stack.push(xmlTag); } - if (xmlTag.isClose()) { - if (xmlTag.getName().equals(openTag.getName())) + XmlTag foundTag = findOpenTag(xmlTag, stack); + if (foundTag == null) { - if (level == 0) + if (xmlTag.getName().equals(openTag.getName())) { closeTag = xmlTag; closeTag.setOpenTag(openTag); } + else if (HtmlHandler.requiresCloseTag(openTag.getName()) == false) + { + // no closeTag for current openTag (allowed) + closeTag = openTag; + } else { - level--; + // no closeTag for current openTag (invalid structure) + // thus reset state + openTag = null; + closeTag = null; } } } } - if ((openTag != null) && (closeTag != null) && (level == 0)) + if ((openTag != null) && (closeTag != null)) { TagTester tester = new TagTester(parser, openTag, closeTag); testers.add(tester); openTag = null; closeTag = null; } - else if (openTag != null && !HtmlHandler.requiresCloseTag(openTag.getName())) - { - TagTester tester = new TagTester(parser, openTag, openTag); - testers.add(tester); - openTag = null; - closeTag = null; - } if (stopAfterFirst && (closeTag != null)) { @@ -694,4 +588,33 @@ public class TagTester return testers; } -} + + /** + * find the correct openTag to the given closeTag and remove all unclosed openTags between both + * in given array {@code stack} + * + * @param closeTag + * tag to search for corresponding openTag + * @param stack + * array of unclosed openTags + * @return corresponding openTag or {@code null} + */ + private static XmlTag findOpenTag(XmlTag closeTag, Stack<XmlTag> stack) + { + while (stack.size() > 0) + { + XmlTag popped = stack.pop(); + if (popped.getName().equals(closeTag.getName())) + { + return popped; + } + } + return null; + } + + // Remove when migration to Java 8 is completed + private interface Predicate<T> + { + boolean apply(T t); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/c6f3efb7/wicket-core/src/test/java/org/apache/wicket/util/tester/TagTesterTest.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/util/tester/TagTesterTest.java b/wicket-core/src/test/java/org/apache/wicket/util/tester/TagTesterTest.java index e7863a8..8834e4a 100644 --- a/wicket-core/src/test/java/org/apache/wicket/util/tester/TagTesterTest.java +++ b/wicket-core/src/test/java/org/apache/wicket/util/tester/TagTesterTest.java @@ -38,7 +38,17 @@ public class TagTesterTest extends Assert "<ajax-response><component id='comp1'><![CDATA[<div class='cls' id='compId'></div>]]></component></ajax-response>"; // WICKET-5874 - private static final String NON_CLOSED_INPUT = "<p><input wicket:id=\"wicketId\" type=\"text\"></p>"; + private static final String NON_CLOSED_INPUT = "<p wicket:id=\"p\"><input wicket:id=\"wicketId\" type=\"text\"></p>"; + + /** + * WICKET-6278 + */ + @Test + public void tagNoRequiredClose() { + TagTester tester = TagTester.createTagByAttribute(NON_CLOSED_INPUT, "wicket:id", "p"); + + assertEquals("<input wicket:id=\"wicketId\" type=\"text\">", tester.getValue()); + } /** * https://issues.apache.org/jira/browse/WICKET-5874