You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by jd...@apache.org on 2011/03/21 22:54:41 UTC

svn commit: r1083980 - in /wicket/trunk/wicket-core/src: main/java/org/apache/wicket/markup/ main/java/org/apache/wicket/markup/parser/ main/java/org/apache/wicket/markup/parser/filter/ test/java/org/apache/wicket/ajax/ test/java/org/apache/wicket/mark...

Author: jdonnerstag
Date: Mon Mar 21 21:54:40 2011
New Revision: 1083980

URL: http://svn.apache.org/viewvc?rev=1083980&view=rev
Log:
- fixed test case 
- some more changes to the "framework"

Will adjust and apply Pedro's patch next
Issue: WICKET-3433

Added:
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/HtmlSpecialTag.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/TagStack.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/ConditionalCommentFilter.java
Modified:
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/AbstractMarkupParser.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/ComponentTag.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupException.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupParser.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupStream.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/AbstractMarkupFilter.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/IXmlPullParser.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/XmlPullParser.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/XmlTag.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/HtmlHandler.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/OpenCloseTagExpander.java
    wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/RootMarkupFilter.java
    wicket/trunk/wicket-core/src/test/java/org/apache/wicket/ajax/MockComponent1-expected.html
    wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/MarkupParserTest.java
    wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/html/panel/PanelTest.java
    wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/parser/XmlPullParserTest.java

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/AbstractMarkupParser.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/AbstractMarkupParser.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/AbstractMarkupParser.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/AbstractMarkupParser.java Mon Mar 21 21:54:40 2011
@@ -209,9 +209,9 @@ public abstract class AbstractMarkupPars
 	 * @return The next tag
 	 * @throws ParseException
 	 */
-	private ComponentTag getNextTag() throws ParseException
+	private MarkupElement getNextTag() throws ParseException
 	{
-		return (ComponentTag)markupFilterChain.nextElement();
+		return markupFilterChain.nextElement();
 	}
 
 	/**
@@ -225,52 +225,62 @@ public abstract class AbstractMarkupPars
 			int size = markup.size();
 
 			// Loop through tags
-			ComponentTag tag;
-			while (null != (tag = getNextTag()))
+			MarkupElement elem;
+			while (null != (elem = getNextTag()))
 			{
-				boolean add = (tag.getId() != null);
-				if (!add && tag.isClose())
+				if (elem instanceof HtmlSpecialTag)
 				{
-					add = ((tag.getOpenTag() != null) && (tag.getOpenTag().getId() != null));
+					elem = new ComponentTag(((HtmlSpecialTag)elem).getXmlTag());
 				}
 
-				// Add tag to list?
-				if (add || tag.isModified() || (markup.size() != size))
+				if (elem instanceof ComponentTag)
 				{
-					// Add text from last position to the current tag position
-					CharSequence text = xmlParser.getInputFromPositionMarker(tag.getPos());
-					if (text.length() > 0)
-					{
-						text = handleRawText(text.toString());
+					ComponentTag tag = (ComponentTag)elem;
 
-						// Make sure you add it at the correct location.
-						// IMarkupFilters might have added elements as well.
-						markup.addMarkupElement(size, new RawMarkup(text));
+					boolean add = (tag.getId() != null);
+					if (!add && tag.isClose())
+					{
+						add = ((tag.getOpenTag() != null) && (tag.getOpenTag().getId() != null));
 					}
 
-					xmlParser.setPositionMarker();
-
-					if (add)
+					// Add tag to list?
+					if (add || tag.isModified() || (markup.size() != size))
 					{
-						// Add to the markup unless the tag has been flagged as
-						// to be removed from the markup. (e.g. <wicket:remove>
-						if (tag.isIgnore() == false)
+						// Add text from last position to the current tag position
+						CharSequence text = xmlParser.getInputFromPositionMarker(tag.getPos());
+						if (text.length() > 0)
 						{
-							markup.addMarkupElement(tag);
+							text = handleRawText(text.toString());
+
+							// Make sure you add it at the correct location.
+							// IMarkupFilters might have added elements as well.
+							markup.addMarkupElement(size, new RawMarkup(text));
+						}
+
+						xmlParser.setPositionMarker();
+
+						if (add)
+						{
+							// Add to the markup unless the tag has been flagged as
+							// to be removed from the markup. (e.g. <wicket:remove>
+							if (tag.isIgnore() == false)
+							{
+								markup.addMarkupElement(tag);
+							}
+						}
+						else if (tag.isModified())
+						{
+							markup.addMarkupElement(new RawMarkup(tag.toCharSequence()));
+						}
+						else
+						{
+							xmlParser.setPositionMarker(tag.getPos());
 						}
 					}
-					else if (tag.isModified())
-					{
-						markup.addMarkupElement(new RawMarkup(tag.toCharSequence()));
-					}
-					else
-					{
-						xmlParser.setPositionMarker(tag.getPos());
-					}
-				}
 
-				// always remember the latest index (size)
-				size = markup.size();
+					// always remember the latest index (size)
+					size = markup.size();
+				}
 			}
 		}
 		catch (final ParseException ex)
@@ -314,7 +324,12 @@ public abstract class AbstractMarkupPars
 	 */
 	protected void postProcess(final Markup markup)
 	{
-		markupFilterChain.postProcess(markup);
+		IMarkupFilter filter = markupFilterChain;
+		while (filter != null)
+		{
+			filter.postProcess(markup);
+			filter = filter.getNextFilter();
+		}
 	}
 
 	/**

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/ComponentTag.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/ComponentTag.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/ComponentTag.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/ComponentTag.java Mon Mar 21 21:54:40 2011
@@ -65,11 +65,8 @@ public class ComponentTag extends Markup
 	/** Render the tag as RawMarkup even if no Component can be found */
 	public final static int RENDER_RAW = 0x0020;
 
-	/**
-	 * Assuming this is a open (or open-close) tag, 'closes' refers to the ComponentTag which closes
-	 * it.
-	 */
-	private ComponentTag closes;
+	/** If close tag, than reference to the corresponding close tag */
+	private ComponentTag openTag;
 
 	/** The underlying xml tag */
 	protected final XmlTag xmlTag;
@@ -223,7 +220,7 @@ public class ComponentTag extends Markup
 	{
 		if (open instanceof ComponentTag)
 		{
-			return (closes == open) || getXmlTag().closes(((ComponentTag)open).getXmlTag());
+			return (openTag == open) || getXmlTag().closes(((ComponentTag)open).getXmlTag());
 		}
 
 		return false;
@@ -317,7 +314,7 @@ public class ComponentTag extends Markup
 	 */
 	public final ComponentTag getOpenTag()
 	{
-		return closes;
+		return openTag;
 	}
 
 	/**
@@ -604,7 +601,7 @@ public class ComponentTag extends Markup
 	 */
 	public final void setOpenTag(final ComponentTag tag)
 	{
-		closes = tag;
+		openTag = tag;
 		getXmlTag().setOpenTag(tag.getXmlTag());
 	}
 

Added: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/HtmlSpecialTag.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/HtmlSpecialTag.java?rev=1083980&view=auto
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/HtmlSpecialTag.java (added)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/HtmlSpecialTag.java Mon Mar 21 21:54:40 2011
@@ -0,0 +1,237 @@
+/*
+ * 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.wicket.markup;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.markup.parser.IXmlPullParser.HttpTagType;
+import org.apache.wicket.markup.parser.XmlTag;
+import org.apache.wicket.markup.parser.XmlTag.TagType;
+import org.apache.wicket.request.Response;
+
+
+/**
+ * 
+ * @author Juergen Donnerstag
+ */
+public class HtmlSpecialTag extends MarkupElement
+{
+	/** The underlying xml tag */
+	protected final XmlTag xmlTag;
+
+	/** Boolean flags. See above */
+	private int flags = 0;
+
+	private final HttpTagType httpTagType;
+
+	/**
+	 * Construct.
+	 * 
+	 * @param tag
+	 *            The underlying xml tag
+	 * @param httpTagType
+	 */
+	public HtmlSpecialTag(final XmlTag tag, final HttpTagType httpTagType)
+	{
+		xmlTag = tag.makeImmutable();
+		this.httpTagType = httpTagType;
+	}
+
+	/**
+	 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
+	 * 
+	 * @param flag
+	 *            The flag to set
+	 * @param set
+	 *            True to turn the flag on, false to turn it off
+	 */
+	public final void setFlag(final int flag, final boolean set)
+	{
+		if (set)
+		{
+			flags |= flag;
+		}
+		else
+		{
+			flags &= ~flag;
+		}
+	}
+
+	/**
+	 * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
+	 * 
+	 * @param flag
+	 *            The flag to test
+	 * @return True if the flag is set
+	 */
+	public final boolean getFlag(final int flag)
+	{
+		return (flags & flag) != 0;
+	}
+
+	/**
+	 * Gets the length of the tag in characters.
+	 * 
+	 * @return The tag's length
+	 */
+	public final int getLength()
+	{
+		return xmlTag.getLength();
+	}
+
+	/**
+	 * @see org.apache.wicket.markup.parser.XmlTag#getPos()
+	 * @return Tag location (index in input string)
+	 */
+	public final int getPos()
+	{
+		return xmlTag.getPos();
+	}
+
+	/**
+	 * @return the tag type (OPEN, CLOSE or OPEN_CLOSE).
+	 */
+	public final TagType getType()
+	{
+		return xmlTag.getType();
+	}
+
+	/**
+	 * @see org.apache.wicket.markup.parser.XmlTag#isClose()
+	 * @return True if this tag is a close tag
+	 */
+	public final boolean isClose()
+	{
+		return xmlTag.isClose();
+	}
+
+	/**
+	 * @see org.apache.wicket.markup.parser.XmlTag#isOpen()
+	 * @return True if this tag is an open tag
+	 */
+	public final boolean isOpen()
+	{
+		return xmlTag.isOpen();
+	}
+
+	/**
+	 * @see org.apache.wicket.markup.parser.XmlTag#isOpenClose()
+	 * @return True if this tag is an open and a close tag
+	 */
+	public final boolean isOpenClose()
+	{
+		return xmlTag.isOpenClose();
+	}
+
+	/**
+	 * Copies all internal properties from this tag to <code>dest</code>. This is basically cloning
+	 * without instance creation.
+	 * 
+	 * @param dest
+	 *            tag whose properties will be set
+	 */
+	void copyPropertiesTo(final HtmlSpecialTag dest)
+	{
+		dest.flags = flags;
+	}
+
+	@Override
+	public CharSequence toCharSequence()
+	{
+		return xmlTag.toCharSequence();
+	}
+
+	/**
+	 * Converts this object to a string representation.
+	 * 
+	 * @return String version of this object
+	 */
+	@Override
+	public final String toString()
+	{
+		return "" + httpTagType + ": '" + xmlTag.toString() + "'";
+	}
+
+	/**
+	 * Write the tag to the response
+	 * 
+	 * @param response
+	 *            The response to write to
+	 * @param stripWicketAttributes
+	 *            if true, wicket:id are removed from output
+	 * @param namespace
+	 *            Wicket's namespace to use
+	 */
+	public final void writeOutput(final Response response, final boolean stripWicketAttributes,
+		final String namespace)
+	{
+		response.write(toString());
+	}
+
+	/**
+	 * Converts this object to a string representation including useful information for debugging
+	 * 
+	 * @return String version of this object
+	 */
+	@Override
+	public final String toUserDebugString()
+	{
+		return xmlTag.toUserDebugString();
+	}
+
+	/**
+	 * @return Returns the underlying xml tag.
+	 */
+	public final XmlTag getXmlTag()
+	{
+		return xmlTag;
+	}
+
+	@Override
+	public boolean equalTo(final MarkupElement element)
+	{
+		if (element instanceof HtmlSpecialTag)
+		{
+			final HtmlSpecialTag that = (HtmlSpecialTag)element;
+			return getXmlTag().equalTo(that.getXmlTag());
+		}
+		return false;
+	}
+
+	/**
+	 * For subclasses to override. Gets called just before a Component gets rendered. It is
+	 * guaranteed that the markupStream is set on the Component and determineVisibility is not yet
+	 * called.
+	 * 
+	 * @param component
+	 *            The component that is about to be rendered
+	 * @param markupStream
+	 *            The current amrkup stream
+	 */
+	public void onBeforeRender(final Component component, final MarkupStream markupStream)
+	{
+	}
+
+	/**
+	 * Gets httpTagType.
+	 * 
+	 * @return httpTagType
+	 */
+	public final HttpTagType getHttpTagType()
+	{
+		return httpTagType;
+	}
+}

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupException.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupException.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupException.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupException.java Mon Mar 21 21:54:40 2011
@@ -98,6 +98,18 @@ public final class MarkupException exten
 	}
 
 	/**
+	 * Construct.
+	 * 
+	 * @param markup
+	 * @param message
+	 * @param cause
+	 */
+	public MarkupException(final Markup markup, final String message, final Throwable cause)
+	{
+		this(new MarkupStream(markup).setCurrentIndex(markup.size() - 1), message, cause);
+	}
+
+	/**
 	 * @return Returns the MarkupStream.
 	 */
 	public MarkupStream getMarkupStream()

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupParser.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupParser.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupParser.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupParser.java Mon Mar 21 21:54:40 2011
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import org.apache.wicket.Page;
 import org.apache.wicket.markup.parser.IMarkupFilter;
 import org.apache.wicket.markup.parser.IXmlPullParser;
+import org.apache.wicket.markup.parser.filter.ConditionalCommentFilter;
 import org.apache.wicket.markup.parser.filter.EnclosureHandler;
 import org.apache.wicket.markup.parser.filter.HeadForceTagIdHandler;
 import org.apache.wicket.markup.parser.filter.HtmlHandler;
@@ -182,6 +183,7 @@ public class MarkupParser extends Abstra
 		filters.add(new RelativePathPrefixHandler());
 		filters.add(new EnclosureHandler());
 		filters.add(new StyleAndScriptIdentifier(markup));
+		filters.add(new ConditionalCommentFilter());
 
 		return filters;
 	}

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupStream.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupStream.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupStream.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/MarkupStream.java Mon Mar 21 21:54:40 2011
@@ -340,11 +340,13 @@ public class MarkupStream
 	/**
 	 * @param currentIndex
 	 *            New current index in the stream
+	 * @return this
 	 */
-	public void setCurrentIndex(final int currentIndex)
+	public MarkupStream setCurrentIndex(final int currentIndex)
 	{
 		current = get(currentIndex);
 		this.currentIndex = currentIndex;
+		return this;
 	}
 
 	/**

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/AbstractMarkupFilter.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/AbstractMarkupFilter.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/AbstractMarkupFilter.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/AbstractMarkupFilter.java Mon Mar 21 21:54:40 2011
@@ -19,8 +19,11 @@ package org.apache.wicket.markup.parser;
 import java.text.ParseException;
 
 import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.HtmlSpecialTag;
 import org.apache.wicket.markup.Markup;
 import org.apache.wicket.markup.MarkupElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 
 /**
@@ -31,6 +34,9 @@ import org.apache.wicket.markup.MarkupEl
  */
 public abstract class AbstractMarkupFilter implements IMarkupFilter
 {
+	/** Log. */
+	private static final Logger log = LoggerFactory.getLogger(AbstractMarkupFilter.class);
+
 	/** The next MarkupFilter in the chain */
 	private IMarkupFilter parent;
 
@@ -70,9 +76,16 @@ public abstract class AbstractMarkupFilt
 	public MarkupElement nextElement() throws ParseException
 	{
 		MarkupElement elem = getNextFilter().nextElement();
-		if ((elem != null) && (elem instanceof ComponentTag))
+		if (elem != null)
 		{
-			elem = onComponentTag((ComponentTag)elem);
+			if (elem instanceof ComponentTag)
+			{
+				elem = onComponentTag((ComponentTag)elem);
+			}
+			else if (elem instanceof HtmlSpecialTag)
+			{
+				elem = onSpecialTag((HtmlSpecialTag)elem);
+			}
 		}
 		return elem;
 	}
@@ -109,11 +122,11 @@ public abstract class AbstractMarkupFilt
 	 * @return Usually the same as the tag attribute
 	 * @throws ParseException
 	 */
-// Not yet used
-// protected MarkupElement onSpecialTag(final ComponentTag tag) throws ParseException
-// {
-// return tag;
-// }
+	protected MarkupElement onSpecialTag(final HtmlSpecialTag tag) throws ParseException
+	{
+		log.error(tag.toString());
+		return tag;
+	}
 
 	/**
 	 * Invoked if current element is raw markup
@@ -136,9 +149,6 @@ public abstract class AbstractMarkupFilt
 		return count++;
 	}
 
-	/**
-	 * Noop by default
-	 */
 	public void postProcess(final Markup markup)
 	{
 	}

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/IXmlPullParser.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/IXmlPullParser.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/IXmlPullParser.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/IXmlPullParser.java Mon Mar 21 21:54:40 2011
@@ -32,7 +32,7 @@ import org.apache.wicket.util.resource.R
 public interface IXmlPullParser
 {
 	/** The last element found */
-	public enum ELEMENT_TYPE {
+	public enum HttpTagType {
 		/** next() must be called at least once for the Type to be valid */
 		NOT_INITIALIZED,
 
@@ -48,6 +48,9 @@ public interface IXmlPullParser
 		/** <!--[if ] ... --> */
 		CONDITIONAL_COMMENT,
 
+		/** <![endif]--> */
+		CONDITIONAL_COMMENT_ENDIF,
+
 		/** <![CDATA[ .. ]]> */
 		CDATA,
 
@@ -155,7 +158,7 @@ public interface IXmlPullParser
 	 * @return o, if end of file. Else a TAG, COMMENT etc.
 	 * @throws ParseException
 	 */
-	ELEMENT_TYPE next() throws ParseException;
+	HttpTagType next() throws ParseException;
 
 	/**
 	 * 

Added: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/TagStack.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/TagStack.java?rev=1083980&view=auto
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/TagStack.java (added)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/TagStack.java Mon Mar 21 21:54:40 2011
@@ -0,0 +1,231 @@
+/*
+ * 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.wicket.markup.parser;
+
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.WicketParseException;
+import org.apache.wicket.markup.parser.filter.HtmlHandler;
+import org.apache.wicket.util.collections.ArrayListStack;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Stack to push and pop HTML elements asserting its structure.
+ */
+public class TagStack
+{
+	private static final Logger log = LoggerFactory.getLogger(HtmlHandler.class);
+
+	/** Map of simple tags. */
+	private static final Map<String, Boolean> doesNotRequireCloseTag = new HashMap<String, Boolean>();
+
+	static
+	{
+		// Tags which are allowed not be closed in HTML
+		doesNotRequireCloseTag.put("p", Boolean.TRUE);
+		doesNotRequireCloseTag.put("br", Boolean.TRUE);
+		doesNotRequireCloseTag.put("img", Boolean.TRUE);
+		doesNotRequireCloseTag.put("input", Boolean.TRUE);
+		doesNotRequireCloseTag.put("hr", Boolean.TRUE);
+		doesNotRequireCloseTag.put("link", Boolean.TRUE);
+		doesNotRequireCloseTag.put("meta", Boolean.TRUE);
+	}
+
+	/** Tag stack to find balancing tags */
+	final private ArrayListStack<ComponentTag> stack = new ArrayListStack<ComponentTag>();
+	private boolean debug;
+
+	/**
+	 * Assert that tag has no mismatch error. If the parameter is an open tag, just push it on stack
+	 * to be tested latter.
+	 * 
+	 * @param tag
+	 * @throws ParseException
+	 */
+	public void assertValidInStack(ComponentTag tag) throws ParseException
+	{
+		// Get the next tag. If null, no more tags are available
+		if (tag == null)
+		{
+			validate();
+			return;
+		}
+
+		if (log.isDebugEnabled() && debug)
+		{
+			log.debug("tag: " + tag.toUserDebugString() + ", stack: " + stack);
+		}
+
+		// Check tag type
+		if (tag.isOpen())
+		{
+			// Push onto stack
+			stack.push(tag);
+		}
+		else if (tag.isClose())
+		{
+			assertOpenTagFor(tag);
+		}
+		else if (tag.isOpenClose())
+		{
+			// Tag closes itself
+			tag.setOpenTag(tag);
+		}
+	}
+
+	/**
+	 * Bind close tag with its open tag and pop it from the stack.
+	 * 
+	 * @param closeTag
+	 * @throws ParseException
+	 */
+	private void assertOpenTagFor(ComponentTag closeTag) throws ParseException
+	{
+		// Check that there is something on the stack
+		if (stack.size() > 0)
+		{
+			// Pop the top tag off the stack
+			ComponentTag top = stack.pop();
+
+			// If the name of the current close tag does not match the
+			// tag on the stack then we may have a mismatched close tag
+			boolean mismatch = !hasEqualTagName(top, closeTag);
+
+			if (mismatch)
+			{
+				top.setHasNoCloseTag(true);
+
+				// Pop any simple tags off the top of the stack
+				while (mismatch && !requiresCloseTag(top.getName()))
+				{
+					top.setHasNoCloseTag(true);
+
+					// Pop simple tag
+					if (stack.isEmpty())
+					{
+						break;
+					}
+					top = stack.pop();
+
+					// Does new top of stack mismatch too?
+					mismatch = !hasEqualTagName(top, closeTag);
+				}
+
+				// If adjusting for simple tags did not fix the problem,
+				// it must be a real mismatch.
+				if (mismatch)
+				{
+					throw new ParseException("Tag " + top.toUserDebugString() +
+						" has a mismatched close tag at " + closeTag.toUserDebugString(),
+						top.getPos());
+				}
+			}
+
+			// Tag matches, so add pointer to matching tag
+			closeTag.setOpenTag(top);
+		}
+		else
+		{
+			throw new WicketParseException("Tag does not have a matching open tag:", closeTag);
+		}
+	}
+
+	private void validate() throws WicketParseException
+	{
+		ComponentTag top = getNotClosedTag();
+		if (top != null)
+		{
+			throw new WicketParseException("Tag does not have a close tag:", top);
+		}
+	}
+
+	/**
+	 * @return not closed tag
+	 */
+	public ComponentTag getNotClosedTag()
+	{
+		// No more tags from the markup.
+		// If there's still a non-simple tag left, it's an error
+		if (stack.size() > 0)
+		{
+			for (int i = 0; i < stack.size(); i++)
+			{
+				ComponentTag tag = stack.get(i);
+				if (!requiresCloseTag(tag.getName()))
+				{
+					stack.pop();
+				}
+				else
+				{
+					return tag;
+				}
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Configure this stack to call log.debug at operations
+	 */
+	public void debug()
+	{
+		debug = true;
+	}
+
+	/**
+	 * Gets whether this tag does not require a closing tag.
+	 * 
+	 * @param name
+	 *            The tag's name, e.g. a, br, div, etc.
+	 * @return True if this tag does not require a closing tag
+	 */
+	public static boolean requiresCloseTag(final String name)
+	{
+		return doesNotRequireCloseTag.get(name.toLowerCase()) == null;
+	}
+
+	/**
+	 * Compare tag name including namespace
+	 * 
+	 * @param tag1
+	 * @param tag2
+	 * @return true if name and namespace are equal
+	 */
+	public static boolean hasEqualTagName(final ComponentTag tag1, final ComponentTag tag2)
+	{
+		if (!tag1.getName().equalsIgnoreCase(tag2.getName()))
+		{
+			return false;
+		}
+
+		if ((tag1.getNamespace() == null) && (tag2.getNamespace() == null))
+		{
+			return true;
+		}
+
+		if ((tag1.getNamespace() != null) && (tag2.getNamespace() != null))
+		{
+			return tag1.getNamespace().equalsIgnoreCase(tag2.getNamespace());
+		}
+
+		return false;
+	}
+}

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/XmlPullParser.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/XmlPullParser.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/XmlPullParser.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/XmlPullParser.java Mon Mar 21 21:54:40 2011
@@ -68,9 +68,9 @@ public final class XmlPullParser impleme
 	private CharSequence doctype;
 
 	/** The type of what is in lastText */
-	private ELEMENT_TYPE lastType = ELEMENT_TYPE.NOT_INITIALIZED;
+	private HttpTagType lastType = HttpTagType.NOT_INITIALIZED;
 
-	/** If lastType == TAG, than ... */
+	/** The last tag found */
 	private XmlTag lastTag;
 
 	/**
@@ -136,7 +136,7 @@ public final class XmlPullParser impleme
 
 		input.setPosition(pos);
 		lastText = input.getSubstring(startIndex, pos);
-		lastType = ELEMENT_TYPE.BODY;
+		lastType = HttpTagType.BODY;
 
 		// Check that the tag is properly closed
 		lastPos = input.find('>', lastPos + tagNameLen);
@@ -163,12 +163,12 @@ public final class XmlPullParser impleme
 	 * @return XXX
 	 * @throws ParseException
 	 */
-	public final ELEMENT_TYPE next() throws ParseException
+	public final HttpTagType next() throws ParseException
 	{
 		// Reached end of markup file?
 		if (input.getPosition() >= input.size())
 		{
-			return ELEMENT_TYPE.NOT_INITIALIZED;
+			return HttpTagType.NOT_INITIALIZED;
 		}
 
 		if (skipUntilText != null)
@@ -183,18 +183,19 @@ public final class XmlPullParser impleme
 		// Tag or Body?
 		if (input.charAt(input.getPosition()) != '<')
 		{
+			// It's a BODY
 			if (openBracketIndex == -1)
 			{
 				// There is no next matching tag.
 				lastText = input.getSubstring(-1);
 				input.setPosition(input.size());
-				lastType = ELEMENT_TYPE.BODY;
+				lastType = HttpTagType.BODY;
 				return lastType;
 			}
 
 			lastText = input.getSubstring(openBracketIndex);
 			input.setPosition(openBracketIndex);
-			lastType = ELEMENT_TYPE.BODY;
+			lastType = HttpTagType.BODY;
 			return lastType;
 		}
 
@@ -220,14 +221,6 @@ public final class XmlPullParser impleme
 				input.getPosition());
 		}
 
-		// Handle special tags like <!-- and <![CDATA ...
-		final char firstChar = tagText.charAt(0);
-		if ((firstChar == '!') || (firstChar == '?'))
-		{
-			specialTagHandling(tagText, openBracketIndex, closeBracketIndex);
-			return lastType;
-		}
-
 		// Type of the tag, to be determined next
 		final TagType type;
 
@@ -266,19 +259,31 @@ public final class XmlPullParser impleme
 			}
 		}
 
-		// Parse remaining tag text, obtaining a tag object or null
-		// if it's invalid
-		lastTag = parseTagText(tagText);
-		if (lastTag != null)
-		{
-			// Populate tag fields
-			lastTag.type = type;
-			lastTag.text = new TextSegment(lastText, openBracketIndex, input.getLineNumber(),
+		// Handle special tags like <!-- and <![CDATA ...
+		final char firstChar = tagText.charAt(0);
+		if ((firstChar == '!') || (firstChar == '?'))
+		{
+			specialTagHandling(tagText, openBracketIndex, closeBracketIndex);
+
+			input.countLinesTo(openBracketIndex);
+			TextSegment text = new TextSegment(lastText, openBracketIndex, input.getLineNumber(),
 				input.getColumnNumber());
+			lastTag = new XmlTag(text, type);
+
+			return lastType;
+		}
 
+		TextSegment text = new TextSegment(lastText, openBracketIndex, input.getLineNumber(),
+			input.getColumnNumber());
+		XmlTag tag = new XmlTag(text, type);
+		lastTag = tag;
+
+		// Parse the tag text and populate tag attributes
+		if (parseTagText(tag, tagText))
+		{
 			// Move to position after the tag
 			input.setPosition(closeBracketIndex + 1);
-			lastType = ELEMENT_TYPE.TAG;
+			lastType = HttpTagType.TAG;
 			return lastType;
 		}
 		else
@@ -301,43 +306,62 @@ public final class XmlPullParser impleme
 		// Handle comments
 		if (tagText.startsWith("!--"))
 		{
-			// Normal comment section.
-			// Skip ahead to "-->". Note that you can not simply test for
-			// tagText.endsWith("--") as the comment might contain a '>'
-			// inside.
-			int pos = input.find("-->", openBracketIndex + 1);
-			if (pos == -1)
-			{
-				throw new ParseException("Unclosed comment beginning at" + getLineAndColumnText(),
-					openBracketIndex);
-			}
-
-			pos += 3;
-			lastText = input.getSubstring(openBracketIndex, pos);
-			lastType = ELEMENT_TYPE.COMMENT;
-
-			// Conditional comment? E.g. "<!--[if IE]><a href='test.html'>my link</a><![endif]-->"
-			if (tagText.startsWith("!--[if ") && tagText.endsWith("]") &&
-				lastText.toString().endsWith("<![endif]-->"))
+			// downlevel-revealed conditional comments e.g.: <!--[if (gt IE9)|!(IE)]><!-->
+			if (tagText.contains("![endif]--"))
 			{
-				lastType = ELEMENT_TYPE.CONDITIONAL_COMMENT;
+				lastType = HttpTagType.CONDITIONAL_COMMENT_ENDIF;
+
+				// Move to position after the tag
+				input.setPosition(closeBracketIndex + 1);
+				return;
+			}
+
+			// Conditional comment? E.g.
+			// "<!--[if IE]><a href='test.html'>my link</a><![endif]-->"
+			if (tagText.startsWith("!--[if ") && tagText.endsWith("]"))
+			{
+				int pos = input.find("]-->", openBracketIndex + 1);
+				if (pos == -1)
+				{
+					throw new ParseException("Unclosed conditional comment beginning at" +
+						getLineAndColumnText(), openBracketIndex);
+				}
+
+				pos += 4;
+				lastText = input.getSubstring(openBracketIndex, pos);
 
 				// Actually it is no longer a comment. It is now
 				// up to the browser to select the section appropriate.
 				input.setPosition(closeBracketIndex + 1);
+				lastType = HttpTagType.CONDITIONAL_COMMENT;
 			}
 			else
 			{
+				// Normal comment section.
+				// Skip ahead to "-->". Note that you can not simply test for
+				// tagText.endsWith("--") as the comment might contain a '>'
+				// inside.
+				int pos = input.find("-->", openBracketIndex + 1);
+				if (pos == -1)
+				{
+					throw new ParseException("Unclosed comment beginning at" +
+						getLineAndColumnText(), openBracketIndex);
+				}
+
+				pos += 3;
+				lastText = input.getSubstring(openBracketIndex, pos);
+				lastType = HttpTagType.COMMENT;
 				input.setPosition(pos);
 			}
 			return;
 		}
 
 		// The closing tag of a conditional comment, e.g.
-		// "<!--[if IE]><a href='test.html'>my link</a><![endif]-->"
+		// "<!--[if IE]><a href='test.html'>my link</a><![endif]-->
+		// and also <!--<![endif]-->"
 		if (tagText.equals("![endif]--"))
 		{
-			lastType = ELEMENT_TYPE.CONDITIONAL_COMMENT;
+			lastType = HttpTagType.CONDITIONAL_COMMENT;
 			input.setPosition(closeBracketIndex + 1);
 			return;
 		}
@@ -373,14 +397,14 @@ public final class XmlPullParser impleme
 				input.setPosition(closeBracketIndex + 1);
 
 				lastText = tagText;
-				lastType = ELEMENT_TYPE.CDATA;
+				lastType = HttpTagType.CDATA;
 				return;
 			}
 		}
 
 		if (tagText.charAt(0) == '?')
 		{
-			lastType = ELEMENT_TYPE.PROCESSING_INSTRUCTION;
+			lastType = HttpTagType.PROCESSING_INSTRUCTION;
 
 			// Move to position after the tag
 			input.setPosition(closeBracketIndex + 1);
@@ -389,7 +413,7 @@ public final class XmlPullParser impleme
 
 		if (tagText.startsWith("!DOCTYPE"))
 		{
-			lastType = ELEMENT_TYPE.DOCTYPE;
+			lastType = HttpTagType.DOCTYPE;
 
 			// Get the tagtext between open and close brackets
 			doctype = input.getSubstring(openBracketIndex + 1, closeBracketIndex);
@@ -400,7 +424,7 @@ public final class XmlPullParser impleme
 		}
 
 		// Move to position after the tag
-		lastType = ELEMENT_TYPE.SPECIAL_TAG;
+		lastType = HttpTagType.SPECIAL_TAG;
 		input.setPosition(closeBracketIndex + 1);
 	}
 
@@ -426,7 +450,7 @@ public final class XmlPullParser impleme
 	 */
 	public final XmlTag nextTag() throws ParseException
 	{
-		while (next() != ELEMENT_TYPE.NOT_INITIALIZED)
+		while (next() != HttpTagType.NOT_INITIALIZED)
 		{
 			switch (lastType)
 			{
@@ -576,12 +600,13 @@ public final class XmlPullParser impleme
 	/**
 	 * Parses the text between tags. For example, "a href=foo.html".
 	 * 
+	 * @param tag
 	 * @param tagText
 	 *            The text between tags
-	 * @return A new Tag object or null if the tag is invalid
+	 * @return false in case of an error
 	 * @throws ParseException
 	 */
-	private XmlTag parseTagText(final String tagText) throws ParseException
+	private boolean parseTagText(final XmlTag tag, final String tagText) throws ParseException
 	{
 		// Get the length of the tagtext
 		final int tagTextLength = tagText.length();
@@ -590,8 +615,6 @@ public final class XmlPullParser impleme
 		final TagNameParser tagnameParser = new TagNameParser(tagText);
 		if (tagnameParser.matcher().lookingAt())
 		{
-			final XmlTag tag = new XmlTag();
-
 			// Extract the tag from the pattern matcher
 			tag.name = tagnameParser.getName();
 			tag.namespace = tagnameParser.getNamespace();
@@ -601,7 +624,7 @@ public final class XmlPullParser impleme
 			int pos = tagnameParser.matcher().end(0);
 			if (pos == tagTextLength)
 			{
-				return tag;
+				return true;
 			}
 
 			// Extract attributes
@@ -643,13 +666,13 @@ public final class XmlPullParser impleme
 				// attributes)
 				if (pos == tagTextLength)
 				{
-					return tag;
+					return true;
 				}
 			}
 
-			return tag;
+			return true;
 		}
 
-		return null;
+		return false;
 	}
 }

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/XmlTag.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/XmlTag.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/XmlTag.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/XmlTag.java Mon Mar 21 21:54:40 2011
@@ -19,6 +19,7 @@ package org.apache.wicket.markup.parser;
 import java.util.Iterator;
 import java.util.Map;
 
+import org.apache.wicket.markup.parser.IXmlPullParser.HttpTagType;
 import org.apache.wicket.util.lang.Objects;
 import org.apache.wicket.util.string.AppendingStringBuffer;
 import org.apache.wicket.util.string.StringValue;
@@ -84,6 +85,8 @@ public class XmlTag
 	/** True if this tag is mutable, false otherwise. */
 	private boolean isMutable = true;
 
+	private HttpTagType httpTagType;
+
 	/**
 	 * Construct.
 	 */
@@ -93,6 +96,18 @@ public class XmlTag
 	}
 
 	/**
+	 * Construct.
+	 * 
+	 * @param text
+	 * @param type
+	 */
+	public XmlTag(final TextSegment text, final TagType type)
+	{
+		this.text = text;
+		this.type = type;
+	}
+
+	/**
 	 * Gets whether this tag closes the provided open tag.
 	 * 
 	 * @param open
@@ -285,8 +300,10 @@ public class XmlTag
 	/**
 	 * Makes this tag object immutable by making the attribute map unmodifiable. Immutable tags
 	 * cannot be made mutable again. They can only be copied into new mutable tag objects.
+	 * 
+	 * @return this
 	 */
-	public void makeImmutable()
+	public XmlTag makeImmutable()
 	{
 		if (isMutable)
 		{
@@ -297,6 +314,7 @@ public class XmlTag
 				text = null;
 			}
 		}
+		return this;
 	}
 
 	/**
@@ -618,12 +636,30 @@ public class XmlTag
 		/** Full text of tag. */
 		final CharSequence text;
 
-		TextSegment(CharSequence text, int pos, int line, int col)
+		TextSegment(final CharSequence text, final int pos, final int line, final int col)
 		{
 			this.text = text;
 			this.pos = pos;
 			lineNumber = line;
 			columnNumber = col;
 		}
+
+		/**
+		 * 
+		 * @return The xml markup text
+		 */
+		public final CharSequence getText()
+		{
+			return text;
+		}
+
+		/**
+		 * @see java.lang.Object#toString()
+		 */
+		@Override
+		public String toString()
+		{
+			return text.toString();
+		}
 	}
 }

Added: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/ConditionalCommentFilter.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/ConditionalCommentFilter.java?rev=1083980&view=auto
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/ConditionalCommentFilter.java (added)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/ConditionalCommentFilter.java Mon Mar 21 21:54:40 2011
@@ -0,0 +1,264 @@
+/*
+ * 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.wicket.markup.parser.filter;
+
+import java.text.ParseException;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.HtmlSpecialTag;
+import org.apache.wicket.markup.Markup;
+import org.apache.wicket.markup.MarkupElement;
+import org.apache.wicket.markup.parser.AbstractMarkupFilter;
+import org.apache.wicket.markup.parser.IXmlPullParser.HttpTagType;
+import org.apache.wicket.util.lang.Generics;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Skip duplicated mismatched markup inside conditional comments
+ * 
+ * @author Pedro Santos
+ * @author Juergen Donnerstag
+ */
+public class ConditionalCommentFilter extends AbstractMarkupFilter
+{
+	private static final Logger log = LoggerFactory.getLogger(ConditionalCommentFilter.class);
+
+	private Queue<ComponentTag> queue = new LinkedList<ComponentTag>();
+	private Map<Integer, Set<String>> skipByLevelMap = Generics.newHashMap();
+	private int level;
+
+	@Override
+	protected MarkupElement onSpecialTag(final HtmlSpecialTag tag) throws ParseException
+	{
+		if (tag.getHttpTagType() == HttpTagType.CONDITIONAL_COMMENT)
+		{
+
+		}
+		return tag;
+	}
+
+// public MarkupElement nextTag() throws ParseException
+// {
+// MarkupElement next = null;
+// if (queue.size() > 0)
+// {
+// next = queue.poll();
+// }
+// else
+// {
+// next = getNextFilter().nextTag();
+// }
+// if (next == null)
+// {
+// return null;
+// }
+// else if (next instanceof ConditionalComment)
+// {
+// ConditionalComment conditionalComment = (ConditionalComment)next;
+// if (conditionalComment.isStartIf())
+// {
+// ConditionalTags conditionalTags = new ConditionalTags();
+// conditionalTags.iterate(getNextFilter());
+// ComponentTag mismatch = conditionalTags.getMismatch();
+// if (mismatch != null)
+// {
+//
+// if (mismatch.isOpen())
+// {
+// // start skipping it at this level
+// log.debug("Start to skip: " + mismatch.getName());
+// startSkip(mismatch.getName());
+// }
+// else
+// {
+// if (shouldSkip(mismatch.getName()))
+// {
+// log.debug("Skipping: " + mismatch.getName());
+// // just skipping the close tag
+// }
+// else
+// {
+// throw new ParseException("Not opened tag: " + mismatch,
+// conditionalTags.mismatchTag.getPos());
+// }
+// }
+// }
+// queue.addAll(conditionalTags.getValidTags());
+// // start the recursion
+// return nextTag();
+// }
+// else
+// {
+// // ConditionalTags should have iterated over the close tag already
+// throw new ParseException("Not opened conditional comment close tag",
+// conditionalComment.getPos());
+// }
+// }
+// else
+// {
+// ComponentTag componentTag = (ComponentTag)next;
+// if (componentTag.isOpen())
+// {
+// level++;
+// }
+// else if (componentTag.isClose())
+// {
+// if (shouldSkip(componentTag.getName()))
+// {
+// log.debug("Skipping close tag outside conditional comment: " +
+// componentTag.getName());
+// // tag outside conditional comment are skipped once
+// stopSkipping(componentTag.getName());
+// // skipping
+// return nextTag();
+// }
+// level--;
+// }
+// return next;
+// }
+// }
+//
+// private void startSkip(String tagName)
+// {
+// Set<String> toSkipSet = skipByLevelMap.get(level);
+// if (toSkipSet == null)
+// {
+// skipByLevelMap.put(level, toSkipSet = new HashSet<String>());
+//
+// }
+// toSkipSet.add(tagName);
+// }
+//
+// private void stopSkipping(String tagName)
+// {
+// skipByLevelMap.get(level).remove(tagName);
+// }
+//
+// private boolean shouldSkip(String tagName)
+// {
+// Set<String> skipList = skipByLevelMap.get(level);
+// return skipList != null && skipList.contains(tagName);
+// }
+//
+// /**
+// * Track the markup inside an conditional tag
+// *
+// * @author Pedro Santos
+// */
+// private static class ConditionalTags
+// {
+// private TagStack tagStack = new TagStack();
+// private ArrayList<ComponentTag> tagSequence = new ArrayList<ComponentTag>();
+// private ComponentTag mismatchTag;
+//
+// public void iterate(IMarkupFilter nextFilter) throws ParseException
+// {
+// MarkupElement markupElement = null;
+// do
+// {
+// markupElement = nextFilter.nextTag();
+// if (markupElement instanceof ConditionalComment)
+// {
+// ConditionalComment conditionalComment = (ConditionalComment)markupElement;
+// if (conditionalComment.isStartIf())
+// {
+// throw new ParseException("nested conditional comments are not suported",
+// conditionalComment.getPos());
+// }
+// }
+// else
+// {
+// ComponentTag componentTag = (ComponentTag)markupElement;
+// try
+// {
+// tagStack.assertValidInStack(componentTag);
+// tagSequence.add(componentTag);
+// }
+// catch (ParseException e)
+// {
+// mismatchTag = componentTag;
+// }
+// }
+// }
+// while (markupElement != null && !(markupElement instanceof ConditionalComment));
+// }
+//
+// private List<ComponentTag> getValidTags()
+// {
+// ComponentTag notClosed = tagStack.getNotClosedTag();
+// @SuppressWarnings("unchecked")
+// List<ComponentTag> validTags = (List<ComponentTag>)tagSequence.clone();
+// if (notClosed != null)
+// {
+// validTags.remove(notClosed);
+// }
+// return validTags;
+// }
+//
+// private boolean hasMismatch()
+// {
+// return mismatchTag != null || tagStack.getNotClosedTag() != null;
+// }
+//
+// private ComponentTag getMismatch()
+// {
+// return mismatchTag != null ? mismatchTag : tagStack.getNotClosedTag();
+// }
+//
+// private boolean equalsTo(ConditionalTags other)
+// {
+// if (tagSequence.size() != other.tagSequence.size())
+// {
+// return false;
+// }
+// else
+// {
+// for (int i = 0; i < tagSequence.size(); i++)
+// {
+// if (!tagSequence.get(i).getName().equals(other.tagSequence.get(i).getName()) ||
+// !tagSequence.get(i).getType().equals(other.tagSequence.get(i).getType()))
+// {
+// return false;
+// }
+// }
+// }
+// return true;
+// }
+// }
+
+	/**
+	 * Post-process the markup if necessary
+	 */
+	@Override
+	public void postProcess(final Markup markup)
+	{
+	}
+
+	/**
+	 * Noop
+	 */
+	@Override
+	protected MarkupElement onComponentTag(final ComponentTag tag) throws ParseException
+	{
+		return tag;
+	}
+}

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/HtmlHandler.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/HtmlHandler.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/HtmlHandler.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/HtmlHandler.java Mon Mar 21 21:54:40 2011
@@ -20,10 +20,10 @@ import java.text.ParseException;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.Markup;
 import org.apache.wicket.markup.MarkupElement;
+import org.apache.wicket.markup.MarkupException;
 import org.apache.wicket.markup.WicketParseException;
 import org.apache.wicket.markup.parser.AbstractMarkupFilter;
 import org.apache.wicket.util.collections.ArrayListStack;
@@ -81,8 +81,7 @@ public final class HtmlHandler extends A
 			}
 			else
 			{
-				throw new WicketRuntimeException(new WicketParseException(
-					"Tag does not have a close tag:", top));
+				throw new MarkupException(markup, "Tag does not have a close tag", null);
 			}
 		}
 	}

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/OpenCloseTagExpander.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/OpenCloseTagExpander.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/OpenCloseTagExpander.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/OpenCloseTagExpander.java Mon Mar 21 21:54:40 2011
@@ -22,7 +22,6 @@ import java.util.List;
 
 import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.MarkupElement;
-import org.apache.wicket.markup.RawMarkup;
 import org.apache.wicket.markup.parser.AbstractMarkupFilter;
 import org.apache.wicket.markup.parser.XmlTag.TagType;
 
@@ -47,6 +46,7 @@ public class OpenCloseTagExpander extend
 		// compatible
 		, "span", "p", "strong", "b", "e", "select", "col");
 
+	// temporary storage. Introduce into flow on next request
 	private ComponentTag next = null;
 
 	@Override
@@ -60,19 +60,15 @@ public class OpenCloseTagExpander extend
 			return rtn;
 		}
 
-		// Find the next tag
-		MarkupElement elem;
-		do
-		{
-			elem = getNextFilter().nextElement();
-			if (elem == null)
-			{
-				return null;
-			}
-		}
-		while (elem instanceof RawMarkup);
+		return super.nextElement();
+	}
 
-		ComponentTag tag = (ComponentTag)elem;
+	/**
+	 * 
+	 */
+	@Override
+	protected MarkupElement onComponentTag(final ComponentTag tag) throws ParseException
+	{
 		if (tag.isOpenClose())
 		{
 			String name = tag.getName();
@@ -92,6 +88,7 @@ public class OpenCloseTagExpander extend
 				}
 			}
 		}
+
 		return tag;
 	}
 
@@ -120,13 +117,4 @@ public class OpenCloseTagExpander extend
 	{
 		return replaceForTags.contains(name.toLowerCase());
 	}
-
-	/**
-	 * Noop
-	 */
-	@Override
-	protected MarkupElement onComponentTag(ComponentTag tag) throws ParseException
-	{
-		return tag;
-	}
 }

Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/RootMarkupFilter.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/RootMarkupFilter.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/RootMarkupFilter.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/markup/parser/filter/RootMarkupFilter.java Mon Mar 21 21:54:40 2011
@@ -19,11 +19,13 @@ package org.apache.wicket.markup.parser.
 import java.text.ParseException;
 
 import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.HtmlSpecialTag;
+import org.apache.wicket.markup.Markup;
 import org.apache.wicket.markup.MarkupElement;
 import org.apache.wicket.markup.parser.AbstractMarkupFilter;
 import org.apache.wicket.markup.parser.IMarkupFilter;
 import org.apache.wicket.markup.parser.IXmlPullParser;
-import org.apache.wicket.markup.parser.IXmlPullParser.ELEMENT_TYPE;
+import org.apache.wicket.markup.parser.IXmlPullParser.HttpTagType;
 
 
 /**
@@ -52,16 +54,24 @@ public final class RootMarkupFilter exte
 	@Override
 	public final MarkupElement nextElement() throws ParseException
 	{
-		ELEMENT_TYPE type;
-		while ((type = parser.next()) != ELEMENT_TYPE.TAG)
+		HttpTagType type;
+		while ((type = parser.next()) != HttpTagType.NOT_INITIALIZED)
 		{
-			if (type == ELEMENT_TYPE.NOT_INITIALIZED)
+			if (type == HttpTagType.BODY)
 			{
-				return null;
+				continue;
+			}
+			else if (type == HttpTagType.TAG)
+			{
+				return new ComponentTag(parser.getElement());
+			}
+			else
+			{
+				return new HtmlSpecialTag(parser.getElement(), type);
 			}
 		}
 
-		return new ComponentTag(parser.getElement());
+		return null;
 	}
 
 	/**
@@ -90,4 +100,21 @@ public final class RootMarkupFilter exte
 	{
 		return tag;
 	}
+
+	/**
+	 * Noop
+	 */
+	@Override
+	public final void postProcess(Markup markup)
+	{
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	@Override
+	public String toString()
+	{
+		return parser.toString();
+	}
 }

Modified: wicket/trunk/wicket-core/src/test/java/org/apache/wicket/ajax/MockComponent1-expected.html
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/test/java/org/apache/wicket/ajax/MockComponent1-expected.html?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/test/java/org/apache/wicket/ajax/MockComponent1-expected.html (original)
+++ wicket/trunk/wicket-core/src/test/java/org/apache/wicket/ajax/MockComponent1-expected.html Mon Mar 21 21:54:40 2011
@@ -1,9 +1,11 @@
 <![CDATA[<head xmlns:wicket="http://wicket.apache.org">
-<style type="text/css" id="org-apache-wicket-ajax-MockComponent1-0"><![CDATA[
+<style type="text/css" id="org-apache-wicket-ajax-MockComponent1-0"><!--/*--><![CDATA[/*><!--*/
+
 .onTheFlyClass {
 	background-color: red;
 	color: white;
 	width: 200px;
 }
-]^]^></style>
+
+/*-->]^]^>*/</style>
 </head>]]>
\ No newline at end of file

Modified: wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/MarkupParserTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/MarkupParserTest.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/MarkupParserTest.java (original)
+++ wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/MarkupParserTest.java Mon Mar 21 21:54:40 2011
@@ -23,6 +23,7 @@ import java.util.Locale;
 import junit.framework.Assert;
 
 import org.apache.wicket.WicketTestCase;
+import org.apache.wicket.markup.html.border.Border;
 import org.apache.wicket.markup.html.pages.PageExpiredErrorPage;
 import org.apache.wicket.markup.parser.XmlTag.TagType;
 import org.apache.wicket.markup.parser.filter.WicketTagIdentifier;
@@ -388,7 +389,8 @@ public final class MarkupParserTest exte
 		assertEquals(5, markup.size());
 		assertEquals("html", ((ComponentTag)markup.get(0)).getName());
 		assertEquals("html", ((ComponentTag)markup.get(4)).getName());
-		assertEquals("<![CDATA[... <x a> ...]]>", markup.get(2).toString());
+		assertEquals("<!--/*--><![CDATA[/*><!--*/\n... <x a> ...\n/*-->]]>*/", markup.get(2)
+			.toString());
 	}
 
 	/**
@@ -454,7 +456,7 @@ public final class MarkupParserTest exte
 		tag = markup.get(0);
 		assertEquals("<script>", tag.toString());
 		tag = markup.get(1);
-		assertEquals("<![CDATA[ text ]]>", tag.toString());
+		assertEquals("<!--/*--><![CDATA[/*><!--*/\n text \n/*-->]]>*/", tag.toString());
 		tag = markup.get(2);
 		assertEquals("</script>", tag.toString());
 
@@ -490,7 +492,7 @@ public final class MarkupParserTest exte
 		tag = markup.get(1);
 		assertEquals("<script>", tag.toString());
 		tag = markup.get(2);
-		assertEquals("<![CDATA[ text ]]>", tag.toString());
+		assertEquals("<!--/*--><![CDATA[/*><!--*/\n text \n/*-->]]>*/", tag.toString());
 		tag = markup.get(3);
 		assertEquals("</script>", tag.toString());
 
@@ -499,8 +501,74 @@ public final class MarkupParserTest exte
 		tag = markup.get(1);
 		assertEquals("<script>", tag.toString());
 		tag = markup.get(2);
-		assertEquals("<![CDATA[ text ]]>", tag.toString());
+		assertEquals("<!--/*--><![CDATA[/*><!--*/\n text \n/*-->]]>*/", tag.toString());
 		tag = markup.get(3);
 		assertEquals("</script>", tag.toString());
 	}
+
+
+	/**
+	 * @throws IOException
+	 * @throws ResourceStreamNotFoundException
+	 * @throws ParseException
+	 */
+	public void testParseConditionalComment() throws IOException, ResourceStreamNotFoundException,
+		ParseException
+	{
+		String x = "  <!--[if IE]>\r\n" + //
+			"    <a href=\"SimplePage_3.html\">Link</a>\r\n" + //
+			"  <![endif]-->";
+		MarkupParser parser = new MarkupParser(x);
+		Markup markup = parser.parse();
+		assertEquals(x, markup.toString(true));
+	}
+
+	/**
+	 * @throws IOException
+	 * @throws ResourceStreamNotFoundException
+	 */
+	public void testParseTagToBeExpanded() throws IOException, ResourceStreamNotFoundException
+	{
+		String x = "<html xmlns:wicket>\r\n<body>\r\n <span wicket:id=\"myPanel\"/>\r\n</body>\r\n</html>\r\n";
+		MarkupParser parser = new MarkupParser(x);
+		Markup markup = parser.parse();
+		assertEquals(
+			"<html xmlns:wicket>\r\n<body>\r\n <span wicket:id=\"myPanel\"></span>\r\n</body>\r\n</html>\r\n",
+			markup.toString(true));
+	}
+
+	/**
+	 * @throws IOException
+	 * @throws ResourceStreamNotFoundException
+	 */
+	public void testParseBorderSintax() throws IOException, ResourceStreamNotFoundException
+	{
+		tester.getApplication().getPageSettings().addComponentResolver(new Border("test_resolver")
+		{
+			/**
+			 * 
+			 */
+			private static final long serialVersionUID = 1L;
+		});
+		String x = "<wicket:border>before body - <wicket:body/> - after body</wicket:border>";
+		MarkupParser parser = new MarkupParser(x);
+		Markup markup = parser.parse();
+		assertEquals(x, markup.toString(true));
+	}
+
+	/**
+	 * WICKET-3500
+	 * 
+	 * @throws IOException
+	 * @throws ResourceStreamNotFoundException
+	 */
+	public void testRawMakupParsingWithStripCommentsSetTrue() throws IOException,
+		ResourceStreamNotFoundException
+	{
+		tester.getApplication().getMarkupSettings().setStripComments(true);
+		String conditionalComment = "\r\n <!--[if IE 6]>\r\n<![endif]-->";
+		MarkupParser parser = new MarkupParser(conditionalComment);
+		Markup markup = parser.parse();
+		assertEquals(conditionalComment, markup.get(0).toString());
+	}
 }

Modified: wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/html/panel/PanelTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/html/panel/PanelTest.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/html/panel/PanelTest.java (original)
+++ wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/html/panel/PanelTest.java Mon Mar 21 21:54:40 2011
@@ -59,7 +59,7 @@ public class PanelTest extends WicketTes
 			hit = true;
 
 			assertNotNull(mex.getMarkupStream());
-			assertTrue(mex.getMessage().contains("Expected close tag for "));
+			assertTrue(mex.getMessage().contains("Tag does not have a close tag"));
 			assertTrue(mex.getMessage().contains("SimplePanel_1.html"));
 		}
 		assertTrue("Did expect a MarkupException", hit);

Modified: wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/parser/XmlPullParserTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/parser/XmlPullParserTest.java?rev=1083980&r1=1083979&r2=1083980&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/parser/XmlPullParserTest.java (original)
+++ wicket/trunk/wicket-core/src/test/java/org/apache/wicket/markup/parser/XmlPullParserTest.java Mon Mar 21 21:54:40 2011
@@ -21,7 +21,7 @@ import java.text.ParseException;
 
 import junit.framework.TestCase;
 
-import org.apache.wicket.markup.parser.IXmlPullParser.ELEMENT_TYPE;
+import org.apache.wicket.markup.parser.IXmlPullParser.HttpTagType;
 import org.apache.wicket.util.resource.StringResourceStream;
 
 /**
@@ -283,9 +283,9 @@ public class XmlPullParserTest extends T
 // assertTrue(tag.isOpen("tag"));
 		tag = parser.nextTag();
 // assertTrue(tag.isOpen("tag"));
-		tag = (XmlTag)parser.nextTag();
+		tag = parser.nextTag();
 		assertTrue(tag.isClose());
-		tag = (XmlTag)parser.nextTag();
+		tag = parser.nextTag();
 		assertNull(tag);
 	}
 
@@ -307,16 +307,16 @@ public class XmlPullParserTest extends T
 	{
 		final XmlPullParser parser = new XmlPullParser();
 		parser.parse("<html><script language=\"JavaScript\">... <x a> ...</script></html>");
-		XmlTag tag = (XmlTag)parser.nextTag();
+		XmlTag tag = parser.nextTag();
 		assertTrue(tag.isOpen());
 		assertEquals("html", tag.getName());
-		tag = (XmlTag)parser.nextTag();
+		tag = parser.nextTag();
 		assertTrue(tag.isOpen());
 		assertEquals("script", tag.getName());
-		tag = (XmlTag)parser.nextTag();
+		tag = parser.nextTag();
 		assertTrue(tag.isClose());
 		assertEquals("script", tag.getName());
-		tag = (XmlTag)parser.nextTag();
+		tag = parser.nextTag();
 		assertTrue(tag.isClose());
 		assertEquals("html", tag.getName());
 	}
@@ -329,13 +329,13 @@ public class XmlPullParserTest extends T
 	{
 		final XmlPullParser parser = new XmlPullParser();
 		parser.parse("<!--[if IE]><a href='test.html'>my link</a><![endif]-->");
-		XmlTag tag = (XmlTag)parser.nextTag();
+		XmlTag tag = parser.nextTag();
 		assertTrue(tag.isOpen());
 		assertEquals("a", tag.getName());
-		tag = (XmlTag)parser.nextTag();
+		tag = parser.nextTag();
 		assertTrue(tag.isClose());
 		assertEquals("a", tag.getName());
-		tag = (XmlTag)parser.nextTag();
+		tag = parser.nextTag();
 		assertNull(tag);
 	}
 
@@ -347,21 +347,21 @@ public class XmlPullParserTest extends T
 	{
 		final XmlPullParser parser = new XmlPullParser();
 		parser.parse("<!--[if IE]><a href='test.html'>my link</a><![endif]-->");
-		ELEMENT_TYPE type = parser.next();
-		assertEquals(type, ELEMENT_TYPE.CONDITIONAL_COMMENT);
+		HttpTagType type = parser.next();
+		assertEquals(type, HttpTagType.CONDITIONAL_COMMENT);
 		type = parser.next();
-		assertEquals(type, ELEMENT_TYPE.TAG);
-		assertTrue(((XmlTag)parser.getElement()).isOpen());
+		assertEquals(type, HttpTagType.TAG);
+		assertTrue((parser.getElement()).isOpen());
 		type = parser.next();
-		assertEquals(type, ELEMENT_TYPE.BODY);
+		assertEquals(type, HttpTagType.BODY);
 		type = parser.next();
-		assertEquals(type, ELEMENT_TYPE.TAG);
-		assertEquals("a", ((XmlTag)parser.getElement()).getName());
-		assertTrue(((XmlTag)parser.getElement()).isClose());
+		assertEquals(type, HttpTagType.TAG);
+		assertEquals("a", (parser.getElement()).getName());
+		assertTrue((parser.getElement()).isClose());
 		type = parser.next();
-		assertEquals(type, ELEMENT_TYPE.CONDITIONAL_COMMENT);
+		assertEquals(type, HttpTagType.CONDITIONAL_COMMENT);
 		type = parser.next();
-		assertEquals(type, ELEMENT_TYPE.NOT_INITIALIZED);
+		assertEquals(type, HttpTagType.NOT_INITIALIZED);
 	}
 
 	/**
@@ -372,17 +372,17 @@ public class XmlPullParserTest extends T
 	{
 		final XmlPullParser parser = new XmlPullParser();
 		parser.parse("<filter-mapping>");
-		XmlTag tag = (XmlTag)parser.nextTag();
+		XmlTag tag = parser.nextTag();
 		assertTrue(tag.isOpen());
 		assertEquals("filter-mapping", tag.getName());
 
 		parser.parse("<filter.mapping>");
-		tag = (XmlTag)parser.nextTag();
+		tag = parser.nextTag();
 		assertTrue(tag.isOpen());
 		assertEquals("filter.mapping", tag.getName());
 
 		parser.parse("<filter_mapping>");
-		tag = (XmlTag)parser.nextTag();
+		tag = parser.nextTag();
 		assertTrue(tag.isOpen());
 		assertEquals("filter_mapping", tag.getName());
 	}
@@ -395,8 +395,30 @@ public class XmlPullParserTest extends T
 	{
 		final XmlPullParser parser = new XmlPullParser();
 		parser.parse("<!DOCTYPE html>");
-		ELEMENT_TYPE type = parser.next();
-		assertEquals(ELEMENT_TYPE.DOCTYPE, type);
+		HttpTagType type = parser.next();
+		assertEquals(HttpTagType.DOCTYPE, type);
 		assertEquals("!DOCTYPE html", parser.getDoctype());
 	}
-}
+
+	/**
+	 * @throws Exception
+	 */
+	public final void testDownlevelRevealedConditionalComments() throws Exception
+	{
+		final XmlPullParser parser = new XmlPullParser();
+		parser.parse("<!--[if (gt IE 9)|!(IE)]><!--><html lang=\"en\" class=\"no-js\"><!--<![endif]--> <span>test</span>");
+		HttpTagType type = parser.next();
+		assertEquals(HttpTagType.CONDITIONAL_COMMENT, type);
+
+		type = parser.next();
+		assertEquals(HttpTagType.COMMENT, type);
+
+		type = parser.next();
+		assertEquals(HttpTagType.TAG, type);
+		XmlTag componentTag = parser.getElement();
+		assertEquals("html", componentTag.getName());
+
+		type = parser.next();
+		assertEquals(HttpTagType.CONDITIONAL_COMMENT_ENDIF, type);
+	}
+}
\ No newline at end of file