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 2007/05/08 19:44:41 UTC

svn commit: r536273 - /incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/

Author: jdonnerstag
Date: Tue May  8 10:44:40 2007
New Revision: 536273

URL: http://svn.apache.org/viewvc?view=rev&rev=536273
Log:
First step in backporting markup fragments

Added:
    incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/Markup.java
      - copied, changed from r535650, incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupFragment.java
    incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupResourceData.java
      - copied, changed from r535650, incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/Markup.java
    incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MergedMarkup.java
Removed:
    incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupFragment.java
Modified:
    incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/IMarkup.java
    incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupCache.java
    incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupElement.java
    incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupParser.java
    incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupStream.java

Modified: incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/IMarkup.java
URL: http://svn.apache.org/viewvc/incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/IMarkup.java?view=diff&rev=536273&r1=536272&r2=536273
==============================================================================
--- incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/IMarkup.java (original)
+++ incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/IMarkup.java Tue May  8 10:44:40 2007
@@ -30,7 +30,7 @@
 public abstract interface IMarkup
 {
 	/** Placeholder that indicates no markup */
-	public static final Markup NO_MARKUP = new Markup();
+	public static final MarkupResourceData NO_MARKUP = new MarkupResourceData();
 
 	/**
 	 * Find the markup element index of the component with 'path'

Copied: incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/Markup.java (from r535650, incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupFragment.java)
URL: http://svn.apache.org/viewvc/incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/Markup.java?view=diff&rev=536273&p1=incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupFragment.java&r1=535650&p2=incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/Markup.java&r2=536273
==============================================================================
--- incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupFragment.java (original)
+++ incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/Markup.java Tue May  8 10:44:40 2007
@@ -18,8 +18,12 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.wicket.util.string.AppendingStringBuffer;
 import org.slf4j.Logger;
@@ -30,35 +34,47 @@
  * A list of markup elements associated with a Markup. Might be all elements of
  * a markup resource, might be just the elements associated with a specific tag.
  * 
- * @see org.apache.wicket.markup.Markup
+ * @see org.apache.wicket.markup.MarkupResourceData
  * @see org.apache.wicket.markup.MarkupElement
  * @see org.apache.wicket.markup.ComponentTag
  * @see org.apache.wicket.markup.RawMarkup
  * 
  * @author Juergen Donnerstag
  */
-public class MarkupFragment
+public class Markup
 {
-	private static final Logger log = LoggerFactory.getLogger(MarkupFragment.class);
+	private static final Logger log = LoggerFactory.getLogger(Markup.class);
 
 	/** Placeholder that indicates no markup */
-	public static final MarkupFragment NO_MARKUP_FRAGMENT = new MarkupFragment(Markup.NO_MARKUP);
+	public static final Markup NO_MARKUP = new Markup(MarkupResourceData.NO_MARKUP_RESOURCE_DATA);
 
 	/** The list of markup elements */
 	private/* final */List markupElements;
 
 	/** The associate markup */
-	private final Markup markup;
+	private final MarkupResourceData markupResourceData;
+
+	/**
+	 * A cache which maps (componentPath + id) to the componentTags index in the
+	 * markup
+	 */
+	private Map componentMap;
+
+	/**
+	 * Used at markup load time to maintain the current component path (not id)
+	 * while adding markup elements to this Markup instance
+	 */
+	private StringBuffer currentPath;
 
 	/**
 	 * Constructor
 	 * 
-	 * @param markup
+	 * @param markupResourceData
 	 *            The associated Markup
 	 */
-	MarkupFragment(final Markup markup)
+	Markup(final MarkupResourceData markupResourceData)
 	{
-		this.markup = markup;
+		this.markupResourceData = markupResourceData;
 		this.markupElements = new ArrayList();
 	}
 
@@ -68,7 +84,7 @@
 	public final String toString()
 	{
 		final AppendingStringBuffer buf = new AppendingStringBuffer(400);
-		buf.append(this.markup.toString());
+		buf.append(this.markupResourceData.toString());
 		buf.append("\n");
 
 		final Iterator iter = this.markupElements.iterator();
@@ -100,9 +116,9 @@
 	 * 
 	 * @return The associated markup
 	 */
-	public final Markup getMarkup()
+	public final MarkupResourceData getMarkupResourceData()
 	{
-		return this.markup;
+		return this.markupResourceData;
 	}
 
 	/**
@@ -122,7 +138,7 @@
 	 * 
 	 * @param markupElement
 	 */
-	final void addMarkupElement(final MarkupElement markupElement)
+	final public void addMarkupElement(final MarkupElement markupElement)
 	{
 		this.markupElements.add(markupElement);
 	}
@@ -133,7 +149,7 @@
 	 * @param pos
 	 * @param markupElement
 	 */
-	final void addMarkupElement(final int pos, final MarkupElement markupElement)
+	final public void addMarkupElement(final int pos, final MarkupElement markupElement)
 	{
 		this.markupElements.add(pos, markupElement);
 	}
@@ -154,6 +170,8 @@
 		}
 
 		this.markupElements = Collections.unmodifiableList(this.markupElements);
+		
+		initialize();
 	}
 
 	/**
@@ -214,5 +232,222 @@
 				index -= 1;
 			}
 		};
+	}
+
+	/**
+	 * Add the tag to the local cache if open or open-close and if wicket:id is
+	 * present
+	 * 
+	 * @param index
+	 * @param tag
+	 */
+	private void addToCache(final int index, final ComponentTag tag)
+	{
+		// Only if the tag has wicket:id="xx" and open or open-close
+		if ((tag.isOpen() || tag.isOpenClose()) && tag.getAttributes().containsKey(getMarkupResourceData().getWicketId()))
+		{
+			// Add the tag to the cache
+			if (this.componentMap == null)
+			{
+				this.componentMap = new HashMap();
+			}
+
+			/*
+			 * XXX cleanup - this fragment check probably needs to be in
+			 * componenttag.isWantToBeDirectMarkupChild() or something similar
+			 * instead of being here
+			 */
+			final boolean fragment = (tag instanceof WicketTag && ((WicketTag)tag).isFragementTag());
+
+			final String key;
+
+			if (tag.getPath() != null && !fragment/* WICKET-404 */)
+			{
+				key = tag.getPath() + ":" + tag.getId();
+			}
+			else
+			{
+				key = tag.getId();
+			}
+			this.componentMap.put(key, new Integer(index));
+		}
+	}
+
+	/**
+	 * Set the components path within the markup and add the component tag to
+	 * the local cache
+	 * 
+	 * @param componentPath
+	 * @param tag
+	 * @return componentPath
+	 */
+	private StringBuffer setComponentPathForTag(final StringBuffer componentPath,
+			final ComponentTag tag)
+	{
+		// Only if the tag has wicket:id="xx" and open or open-close
+		if ((tag.isOpen() || tag.isOpenClose()) && tag.getAttributes().containsKey(markupResourceData.getWicketId()))
+		{
+			// With open-close the path does not change. It can/will not have
+			// children. The same is true for HTML tags like <br> or <img>
+			// which might not have close tags.
+			if (tag.isOpenClose() || tag.hasNoCloseTag())
+			{
+				// Set the components path.
+				if ((this.currentPath != null) && (this.currentPath.length() > 0))
+				{
+					tag.setPath(this.currentPath.toString());
+				}
+			}
+			else
+			{
+				// Set the components path.
+				if (this.currentPath == null)
+				{
+					this.currentPath = new StringBuffer(100);
+				}
+				else if (this.currentPath.length() > 0)
+				{
+					tag.setPath(this.currentPath.toString());
+					this.currentPath.append(':');
+				}
+
+				// .. and append the tags id to the component path for the
+				// children to come
+				this.currentPath.append(tag.getId());
+			}
+		}
+		else if (tag.isClose() && (this.currentPath != null))
+		{
+			// For example <wicket:message> does not have an id
+			if ((tag.getOpenTag() == null)
+					|| tag.getOpenTag().getAttributes().containsKey(markupResourceData.getWicketId()))
+			{
+				// Remove the last element from the component path
+				int index = this.currentPath.lastIndexOf(":");
+				if (index != -1)
+				{
+					this.currentPath.setLength(index);
+				}
+				else
+				{
+					this.currentPath.setLength(0);
+				}
+			}
+		}
+
+		return this.currentPath;
+	}
+
+	/**
+	 * Create an iterator for the component tags in the markup.
+	 * 
+	 * @param startIndex
+	 *            The index to start with
+	 * @param matchClass
+	 *            Iterate over elements matching the class
+	 * @return ComponentTagIterator
+	 */
+	public Iterator componentTagIterator(final int startIndex, final Class matchClass)
+	{
+		return iterator(startIndex, matchClass);
+	}
+	
+	/**
+	 * Find the markup element index of the component with 'path'
+	 * 
+	 * @param path
+	 *            The component path expression
+	 * @param id
+	 *            The component's id to search for
+	 * @return -1, if not found
+	 */
+	public int findComponentIndex(final String path, final String id)
+	{
+		if ((id == null) || (id.length() == 0))
+		{
+			throw new IllegalArgumentException("Parameter 'id' must not be null");
+		}
+
+		// TODO Post 1.2: A component path e.g. "panel:label" does not match 1:1
+		// with the markup in case of ListView, where the path contains a number
+		// for each list item. E.g. list:0:label. What we currently do is simply
+		// remove the number from the path and hope that no user uses an integer
+		// for a component id. This is a hack only. A much better solution would
+		// delegate to the various components recursivly to search within there
+		// realm only for the components markup. ListItems could then simply
+		// do nothing and delegate to their parents.
+		String completePath = (path == null || path.length() == 0 ? id : path + ":" + id);
+
+		// s/:\d+//g
+		Pattern re = Pattern.compile(":\\d+");
+		Matcher matcher = re.matcher(completePath);
+		completePath = matcher.replaceAll("");
+
+		// All component tags are registered with the cache
+		if (this.componentMap == null)
+		{
+			// not found
+			return -1;
+		}
+
+		final Integer value = (Integer)this.componentMap.get(completePath);
+		if (value == null)
+		{
+			// not found
+			return -1;
+		}
+
+		// return the components position in the markup stream
+		return value.intValue();
+	}
+
+	/**
+	 * @param that
+	 *            The markup to compare with
+	 * @return True if the two markups are equal
+	 */
+	public boolean equalTo(final Markup that)
+	{
+		final MarkupStream thisStream = new MarkupStream(this);
+		final MarkupStream thatStream = new MarkupStream(that);
+
+		// Compare the streams
+		return thisStream.equalTo(thatStream);
+	}
+
+	/**
+	 * Initialize the index where wicket tags can be found
+	 */
+	protected void initialize()
+	{
+		// Reset
+		this.componentMap = null;
+
+		if (markupElements != null)
+		{
+			// HTML tags like <img> may not have a close tag. But because that
+			// can only be detected until later on in the sequential markup
+			// reading loop, we only can do it now.
+			StringBuffer componentPath = null;
+			for (int i = 0; i < size(); i++)
+			{
+				MarkupElement elem = get(i);
+				if (elem instanceof ComponentTag)
+				{
+					ComponentTag tag = (ComponentTag)elem;
+
+					// Set the tags components path
+					componentPath = setComponentPathForTag(componentPath, tag);
+
+					// and add it to the local cache to be found fast if
+					// required
+					addToCache(i, tag);
+				}
+			}
+		}
+
+		// The variable is only needed while adding markup elements.
+		// initialize() is invoked after all elements have been added.
+		this.currentPath = null;
 	}
 }

Modified: incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupCache.java
URL: http://svn.apache.org/viewvc/incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupCache.java?view=diff&rev=536273&r1=536272&r2=536273
==============================================================================
--- incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupCache.java (original)
+++ incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupCache.java Tue May  8 10:44:40 2007
@@ -171,25 +171,24 @@
 		}
 
 		// get the base markup
-		final Markup baseMarkup = getMarkup(container, markup.getResource().getMarkupClass()
+		final Markup baseMarkup = getMarkup(container, markup.getMarkupResourceData().getResource().getMarkupClass()
 				.getSuperclass());
 
 		if (baseMarkup == Markup.NO_MARKUP)
 		{
 			throw new MarkupNotFoundException(
 					"Parent markup of inherited markup not found. Component class: "
-							+ markup.getResource().getContainerInfo().getContainerClass().getName()
+							+ markup.getMarkupResourceData().getResource().getContainerInfo().getContainerClass().getName()
 							+ " Enable debug messages for org.apache.wicket.util.resource.Resource to get a list of all filenames tried.");
 		}
 
-		final CharSequence key = markup.getResource().getCacheKey();
+		final CharSequence key = markup.getMarkupResourceData().getResource().getCacheKey();
 		if (key != null)
 		{
 			// register an after-load listener for base markup. The listener
 			// implementation will remove the derived markup which must be
-			// merged
-			// with the base markup
-			afterLoadListeners.add(baseMarkup.getResource(), new IChangeListener()
+			// merged with the base markup
+			afterLoadListeners.add(baseMarkup.getMarkupResourceData().getResource(), new IChangeListener()
 			{
 				/**
 				 * Make sure there is only one listener per derived markup
@@ -215,9 +214,9 @@
 				{
 					if (log.isDebugEnabled())
 					{
-						log.debug("Remove derived markup from cache: " + markup.getResource());
+						log.debug("Remove derived markup from cache: " + markup.getMarkupResourceData().getResource());
 					}
-					removeMarkup(markup.getResource());
+					removeMarkup(markup.getMarkupResourceData().getResource());
 				}
 			});
 		}

Modified: incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupElement.java
URL: http://svn.apache.org/viewvc/incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupElement.java?view=diff&rev=536273&r1=536272&r2=536273
==============================================================================
--- incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupElement.java (original)
+++ incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupElement.java Tue May  8 10:44:40 2007
@@ -29,7 +29,7 @@
  * Wicket.
  * </ul>
  * 
- * @see Markup
+ * @see MarkupResourceData
  * @see org.apache.wicket.markup.RawMarkup
  * @see ComponentTag
  * @author Jonathan Locke

Modified: incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupParser.java
URL: http://svn.apache.org/viewvc/incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupParser.java?view=diff&rev=536273&r1=536272&r2=536273
==============================================================================
--- incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupParser.java (original)
+++ incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupParser.java Tue May  8 10:44:40 2007
@@ -55,7 +55,7 @@
  * @see IMarkupFilter
  * @see IMarkupParserFactory
  * @see IMarkupSettings
- * @see Markup
+ * @see MarkupResourceData
  * 
  * @author Jonathan Locke
  * @author Juergen Donnerstag
@@ -113,8 +113,10 @@
 		this.xmlParser = xmlParser;
 		this.markupSettings = Application.get().getMarkupSettings();
 
-		this.markup = new Markup();
-		this.markup.setResource(resource);
+		MarkupResourceData markup = new MarkupResourceData();
+		markup.setResource(resource);
+		
+		this.markup = new Markup(markup);
 
 		// Initialize the markup filter chain
 		initializeMarkupFilters();
@@ -128,7 +130,7 @@
 	 */
 	public final void setWicketNamespace(final String namespace)
 	{
-		this.markup.setWicketNamespace(namespace);
+		this.markup.getMarkupResourceData().setWicketNamespace(namespace);
 	}
 
 	/**
@@ -139,7 +141,7 @@
 	 */
 	protected MarkupResourceStream getMarkupResourceStream()
 	{
-		return this.markup.getResource();
+		return this.markup.getMarkupResourceData().getResource();
 	}
 
 	/**
@@ -151,15 +153,17 @@
 		// Chain together all the different markup filters and configure them
 		this.markupFilterChain = xmlParser;
 
-		appendMarkupFilter(new WicketTagIdentifier(markup));
+		MarkupResourceData markupResourceData = this.markup.getMarkupResourceData();
+		
+		appendMarkupFilter(new WicketTagIdentifier(markupResourceData));
 		appendMarkupFilter(new TagTypeHandler());
 		appendMarkupFilter(new HtmlHandler());
 		appendMarkupFilter(new WicketRemoveTagHandler());
 		appendMarkupFilter(new WicketLinkTagHandler());
-		appendMarkupFilter(new WicketNamespaceHandler(markup));
+		appendMarkupFilter(new WicketNamespaceHandler(markupResourceData));
 
 		// Provided the wicket component requesting the markup is known ...
-		final MarkupResourceStream resource = markup.getResource();
+		final MarkupResourceStream resource = markupResourceData.getResource();
 		if (resource != null)
 		{
 			final ContainerInfo containerInfo = resource.getContainerInfo();
@@ -171,7 +175,7 @@
 				// Pages require additional handlers
 				if (Page.class.isAssignableFrom(containerInfo.getContainerClass()))
 				{
-					appendMarkupFilter(new HtmlHeaderSectionHandler(this.markup));
+					appendMarkupFilter(new HtmlHeaderSectionHandler(markup));
 				}
 
 				appendMarkupFilter(new HeadForceTagIdHandler(containerInfo.getContainerClass()));
@@ -257,16 +261,18 @@
 	 */
 	public final Markup parse() throws IOException, ResourceStreamNotFoundException
 	{
+		MarkupResourceData markupResourceData = this.markup.getMarkupResourceData();
+		
 		// Initialize the xml parser
-		this.xmlParser.parse(this.markup.getResource().getInputStream(), this.markupSettings
+		this.xmlParser.parse(markupResourceData.getResource().getInputStream(), this.markupSettings
 				.getDefaultMarkupEncoding());
 
 		// parse the xml markup and tokenize it into wicket relevant markup
 		// elements
 		parseMarkup();
 
-		this.markup.setEncoding(xmlParser.getEncoding());
-		this.markup.setXmlDeclaration(xmlParser.getXmlDeclaration());
+		markupResourceData.setEncoding(xmlParser.getEncoding());
+		markupResourceData.setXmlDeclaration(xmlParser.getXmlDeclaration());
 
 		return this.markup;
 	}
@@ -361,10 +367,10 @@
 				this.markup.addMarkupElement(new RawMarkup(text));
 			}
 
-			this.markup.setEncoding(xmlParser.getEncoding());
-			this.markup.setXmlDeclaration(xmlParser.getXmlDeclaration());
+			this.markup.getMarkupResourceData().setEncoding(xmlParser.getEncoding());
+			this.markup.getMarkupResourceData().setXmlDeclaration(xmlParser.getXmlDeclaration());
 
-			final MarkupStream markupStream = new MarkupStream(markup);
+			final MarkupStream markupStream = new MarkupStream(this.markup);
 			markupStream.setCurrentIndex(this.markup.size() - 1);
 			throw new MarkupException(markupStream, ex.getMessage(), ex);
 		}

Copied: incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupResourceData.java (from r535650, incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/Markup.java)
URL: http://svn.apache.org/viewvc/incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupResourceData.java?view=diff&rev=536273&p1=incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/Markup.java&r1=535650&p2=incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupResourceData.java&r2=536273
==============================================================================
--- incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/Markup.java (original)
+++ incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupResourceData.java Tue May  8 10:44:40 2007
@@ -16,12 +16,6 @@
  */
 package org.apache.wicket.markup;
 
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,15 +30,12 @@
  * @author Jonathan Locke
  * @author Juergen Donnerstag
  */
-public class Markup
+public class MarkupResourceData
 {
-	private static final Logger log = LoggerFactory.getLogger(Markup.class);
+	private static final Logger log = LoggerFactory.getLogger(MarkupResourceData.class);
 
 	/** Placeholder that indicates no markup */
-	public static final Markup NO_MARKUP = new Markup();
-
-	/** The list of markup elements */
-	private MarkupFragment markup;
+	public static final MarkupResourceData NO_MARKUP_RESOURCE_DATA = new MarkupResourceData();
 
 	/** The markup's resource stream for diagnostic purposes */
 	private MarkupResourceStream resource;
@@ -58,81 +49,18 @@
 	/** Wicket namespace: <html xmlns:wicket="http://wicket.apache.org"> */
 	private String wicketNamespace;
 
-	/** == wicketNamespace + ":id" */
+	/** == wicket namespace name + ":id" */
 	private String wicketId;
 
 	/**
-	 * Used at markup load time to maintain the current component path (not id)
-	 * while adding markup elements to this Markup instance
-	 */
-	private StringBuffer currentPath;
-
-	/**
-	 * A cache which maps (componentPath + id) to the componentTags index in the
-	 * markup
-	 */
-	private Map componentMap;
-
-	/**
 	 * Constructor
 	 */
-	Markup()
+	MarkupResourceData()
 	{
-		this.markup = new MarkupFragment(this);
 		setWicketNamespace(ComponentTag.DEFAULT_WICKET_NAMESPACE);
 	}
 
 	/**
-	 * Initialize the index where <head> can be found.
-	 */
-	protected void initialize()
-	{
-		// Reset
-		this.componentMap = null;
-
-		if (markup != null)
-		{
-			// HTML tags like <img> may not have a close tag. But because that
-			// can only be detected until later on in the sequential markup
-			// reading loop, we only can do it now.
-			StringBuffer componentPath = null;
-			for (int i = 0; i < this.markup.size(); i++)
-			{
-				MarkupElement elem = this.markup.get(i);
-				if (elem instanceof ComponentTag)
-				{
-					ComponentTag tag = (ComponentTag)elem;
-
-					// Set the tags components path
-					componentPath = setComponentPathForTag(componentPath, tag);
-
-					// and add it to the local cache to be found fast if
-					// required
-					addToCache(i, tag);
-				}
-			}
-		}
-
-		// The variable is only needed while adding markup elements.
-		// initialize() is invoked after all elements have been added.
-		this.currentPath = null;
-	}
-
-	/**
-	 * @param that
-	 *            The markup to compare with
-	 * @return True if the two markups are equal
-	 */
-	public boolean equalTo(final Markup that)
-	{
-		final MarkupStream thisStream = new MarkupStream(this);
-		final MarkupStream thatStream = new MarkupStream(that);
-
-		// Compare the streams
-		return thisStream.equalTo(thatStream);
-	}
-
-	/**
 	 * @return String representation of markup list
 	 */
 	public String toString()
@@ -148,28 +76,6 @@
 	}
 
 	/**
-	 * @return String representation of markup list
-	 */
-	public String toDebugString()
-	{
-		return this.markup.toString();
-	}
-
-	/**
-	 * For Wicket it would be sufficient for this method to be package
-	 * protected. However to allow wicket-bench easy access to the information
-	 * ...
-	 * 
-	 * @param index
-	 *            Index into markup list
-	 * @return Markup element
-	 */
-	public MarkupElement get(final int index)
-	{
-		return markup.get(index);
-	}
-
-	/**
 	 * Gets the resource that contains this markup
 	 * 
 	 * @return The resource where this markup came from
@@ -180,18 +86,6 @@
 	}
 
 	/**
-	 * For Wicket it would be sufficient for this method to be package
-	 * protected. However to allow wicket-bench easy access to the information
-	 * ...
-	 * 
-	 * @return Number of markup elements
-	 */
-	public int size()
-	{
-		return markup.size();
-	}
-
-	/**
 	 * Return the XML declaration string, in case if found in the markup.
 	 * 
 	 * @return Null, if not found.
@@ -224,54 +118,14 @@
 	}
 
 	/**
-	 * Find the markup element index of the component with 'path'
 	 * 
-	 * @param path
-	 *            The component path expression
-	 * @param id
-	 *            The component's id to search for
-	 * @return -1, if not found
+	 * @return usually it is "wicket:id"
 	 */
-	public int findComponentIndex(final String path, final String id)
+	final public String getWicketId()
 	{
-		if ((id == null) || (id.length() == 0))
-		{
-			throw new IllegalArgumentException("Parameter 'id' must not be null");
-		}
-
-		// TODO Post 1.2: A component path e.g. "panel:label" does not match 1:1
-		// with the markup in case of ListView, where the path contains a number
-		// for each list item. E.g. list:0:label. What we currently do is simply
-		// remove the number from the path and hope that no user uses an integer
-		// for a component id. This is a hack only. A much better solution would
-		// delegate to the various components recursivly to search within there
-		// realm only for the components markup. ListItems could then simply
-		// do nothing and delegate to their parents.
-		String completePath = (path == null || path.length() == 0 ? id : path + ":" + id);
-
-		// s/:\d+//g
-		Pattern re = Pattern.compile(":\\d+");
-		Matcher matcher = re.matcher(completePath);
-		completePath = matcher.replaceAll("");
-
-		// All component tags are registered with the cache
-		if (this.componentMap == null)
-		{
-			// not found
-			return -1;
-		}
-
-		final Integer value = (Integer)this.componentMap.get(completePath);
-		if (value == null)
-		{
-			// not found
-			return -1;
-		}
-
-		// return the components position in the markup stream
-		return value.intValue();
+		return wicketId;
 	}
-
+	
 	/**
 	 * Sets encoding.
 	 * 
@@ -322,170 +176,4 @@
 	{
 		this.resource = resource;
 	}
-
-	/**
-	 * Add a MarkupElement
-	 * 
-	 * @param markupElement
-	 */
-	public final void addMarkupElement(final MarkupElement markupElement)
-	{
-		this.markup.addMarkupElement(markupElement);
-	}
-
-	/**
-	 * Add a MarkupElement
-	 * 
-	 * @param pos
-	 * @param markupElement
-	 */
-	final void addMarkupElement(final int pos, final MarkupElement markupElement)
-	{
-		this.markup.addMarkupElement(pos, markupElement);
-	}
-
-	/**
-	 * Add the tag to the local cache if open or open-close and if wicket:id is
-	 * present
-	 * 
-	 * @param index
-	 * @param tag
-	 */
-	private void addToCache(final int index, final ComponentTag tag)
-	{
-		// Only if the tag has wicket:id="xx" and open or open-close
-		if ((tag.isOpen() || tag.isOpenClose()) && tag.getAttributes().containsKey(wicketId))
-		{
-			// Add the tag to the cache
-			if (this.componentMap == null)
-			{
-				this.componentMap = new HashMap();
-			}
-
-			/*
-			 * XXX cleanup - this fragment check probably needs to be in
-			 * componenttag.isWantToBeDirectMarkupChild() or something similar
-			 * instead of being here
-			 */
-			final boolean fragment = (tag instanceof WicketTag && ((WicketTag)tag).isFragementTag());
-
-			final String key;
-
-			if (tag.getPath() != null && !fragment/* WICKET-404 */)
-			{
-				key = tag.getPath() + ":" + tag.getId();
-			}
-			else
-			{
-				key = tag.getId();
-			}
-			this.componentMap.put(key, new Integer(index));
-		}
-	}
-
-	/**
-	 * Set the components path within the markup and add the component tag to
-	 * the local cache
-	 * 
-	 * @param componentPath
-	 * @param tag
-	 * @return componentPath
-	 */
-	private StringBuffer setComponentPathForTag(final StringBuffer componentPath,
-			final ComponentTag tag)
-	{
-		// Only if the tag has wicket:id="xx" and open or open-close
-		if ((tag.isOpen() || tag.isOpenClose()) && tag.getAttributes().containsKey(wicketId))
-		{
-			// With open-close the path does not change. It can/will not have
-			// children. The same is true for HTML tags like <br> or <img>
-			// which might not have close tags.
-			if (tag.isOpenClose() || tag.hasNoCloseTag())
-			{
-				// Set the components path.
-				if ((this.currentPath != null) && (this.currentPath.length() > 0))
-				{
-					tag.setPath(this.currentPath.toString());
-				}
-			}
-			else
-			{
-				// Set the components path.
-				if (this.currentPath == null)
-				{
-					this.currentPath = new StringBuffer(100);
-				}
-				else if (this.currentPath.length() > 0)
-				{
-					tag.setPath(this.currentPath.toString());
-					this.currentPath.append(':');
-				}
-
-				// .. and append the tags id to the component path for the
-				// children to come
-				this.currentPath.append(tag.getId());
-			}
-		}
-		else if (tag.isClose() && (this.currentPath != null))
-		{
-			// For example <wicket:message> does not have an id
-			if ((tag.getOpenTag() == null)
-					|| tag.getOpenTag().getAttributes().containsKey(wicketId))
-			{
-				// Remove the last element from the component path
-				int index = this.currentPath.lastIndexOf(":");
-				if (index != -1)
-				{
-					this.currentPath.setLength(index);
-				}
-				else
-				{
-					this.currentPath.setLength(0);
-				}
-			}
-		}
-
-		return this.currentPath;
-	}
-
-	/**
-	 * Make all tags immutable and the list of elements unmodifable.
-	 */
-	final void makeImmutable()
-	{
-		this.markup.makeImmutable();
-
-		// We assume all markup elements have now been added. It is
-		// now time to initialize all remaining variables based
-		// on the markup loaded, which could not be initialized
-		// earlier on.
-		initialize();
-	}
-
-	/**
-	 * Reset the markup to its defaults, except for the wicket namespace which
-	 * remains unchanged.
-	 */
-	final void reset()
-	{
-		this.markup = new MarkupFragment(this);
-		this.resource = null;
-		this.xmlDeclaration = null;
-		this.encoding = null;
-		this.currentPath = null;
-	}
-
-	/**
-	 * Create an iterator for the component tags in the markup.
-	 * 
-	 * @param startIndex
-	 *            The index to start with
-	 * @param matchClass
-	 *            Iterate over elements matching the class
-	 * @return ComponentTagIterator
-	 */
-	public Iterator componentTagIterator(final int startIndex, final Class matchClass)
-	{
-		return this.markup.iterator(startIndex, matchClass);
-	}
-}
+}
\ No newline at end of file

Modified: incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupStream.java
URL: http://svn.apache.org/viewvc/incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupStream.java?view=diff&rev=536273&r1=536272&r2=536273
==============================================================================
--- incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupStream.java (original)
+++ incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MarkupStream.java Tue May  8 10:44:40 2007
@@ -60,14 +60,14 @@
 	/**
 	 * Constructor
 	 * 
-	 * @param markup
+	 * @param markupFragment
 	 *            List of markup elements
 	 */
-	public MarkupStream(final Markup markup)
+	public MarkupStream(final Markup markupFragment)
 	{
-		this.markup = markup;
+		this.markup = markupFragment;
 
-		if (markup.size() > 0)
+		if (markupFragment.size() > 0)
 		{
 			current = get(currentIndex);
 		}
@@ -79,7 +79,7 @@
 	 */
 	protected MarkupStream()
 	{
-		markup = null;
+		this.markup = null;
 	}
 
 	/**
@@ -243,7 +243,7 @@
 	 */
 	public final Class getContainerClass()
 	{
-		return markup.getResource().getMarkupClass();
+		return markup.getMarkupResourceData().getResource().getMarkupClass();
 	}
 
 	/**
@@ -263,7 +263,7 @@
 	 */
 	public final String getEncoding()
 	{
-		return markup.getEncoding();
+		return markup.getMarkupResourceData().getEncoding();
 	}
 
 	/**
@@ -271,7 +271,7 @@
 	 */
 	public IResourceStream getResource()
 	{
-		return markup.getResource();
+		return markup.getMarkupResourceData().getResource();
 	}
 
 	/**
@@ -296,7 +296,7 @@
 	 */
 	public final String getWicketNamespace()
 	{
-		return this.markup.getWicketNamespace();
+		return markup.getMarkupResourceData().getWicketNamespace();
 	}
 
 	/**
@@ -306,7 +306,7 @@
 	 */
 	public String getXmlDeclaration()
 	{
-		return markup.getXmlDeclaration();
+		return markup.getMarkupResourceData().getXmlDeclaration();
 	}
 
 	/**

Added: incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MergedMarkup.java
URL: http://svn.apache.org/viewvc/incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MergedMarkup.java?view=auto&rev=536273
==============================================================================
--- incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MergedMarkup.java (added)
+++ incubator/wicket/trunk/jdk-1.4/wicket/src/main/java/org/apache/wicket/markup/MergedMarkup.java Tue May  8 10:44:40 2007
@@ -0,0 +1,397 @@
+/*
+ * 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.Page;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.markup.parser.XmlTag;
+import org.apache.wicket.markup.parser.filter.HtmlHeaderSectionHandler;
+import org.apache.wicket.util.string.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A Markup class which represents merged markup, as it is required for markup
+ * inheritance.
+ * <p>
+ * The Markups are merged at load time. Deep markup hierarchies are supported.
+ * Multiple inheritance is not.
+ * <p>
+ * The markup resource file, which is associated with the markup, will be the
+ * resource of the requested markup file. The base markup resources are not.
+ * <p>
+ * Base Markup must have a &lt;wicket:hild/&gt; tag which the position where the
+ * derived markup is inserted. From the derived markup all tags in between
+ * &lt;wicket:extend&gt; and &lt;/wicket:extend&gt; will be inserted.
+ * <p>
+ * In addition, all &lt;wicket:head> regions are copied as well as the body
+ * onLoad attribute. This allows to develop completely self-contained plug &
+ * play components including javascript etc.
+ * 
+ * @author Juergen Donnerstag
+ */
+public class MergedMarkup extends Markup
+{
+	private final static Logger log = LoggerFactory.getLogger(MergedMarkup.class);
+
+	/**
+	 * Merge inherited and base markup.
+	 * 
+	 * @param markup
+	 *            The inherited markup
+	 * @param baseMarkup
+	 *            The base markup
+	 * @param extendIndex
+	 *            Index where <wicket:extend> has been found
+	 */
+	MergedMarkup(final Markup markup, final Markup baseMarkup, int extendIndex)
+	{
+		super(new MarkupResourceData());
+		
+		// Copy settings from derived markup
+		getMarkupResourceData().setResource(markup.getMarkupResourceData().getResource());
+		getMarkupResourceData().setXmlDeclaration(markup.getMarkupResourceData().getXmlDeclaration());
+		getMarkupResourceData().setEncoding(markup.getMarkupResourceData().getEncoding());
+		getMarkupResourceData().setWicketNamespace(markup.getMarkupResourceData().getWicketNamespace());
+
+		if (log.isDebugEnabled())
+		{
+			String derivedResource = Strings.afterLast(markup.getMarkupResourceData().getResource().toString(), '/');
+			String baseResource = Strings.afterLast(baseMarkup.getMarkupResourceData().getResource().toString(), '/');
+			log.debug("Merge markup: derived markup: " + derivedResource + "; base markup: "
+					+ baseResource);
+		}
+
+		// Merge derived and base markup
+		merge(markup, baseMarkup, extendIndex);
+
+		// Initialize internals based on new markup
+//		initialize();
+
+		if (log.isDebugEnabled())
+		{
+			log.debug("Merge markup: " + toString());
+		}
+	}
+
+	/**
+	 * Merge inherited and base markup.
+	 * 
+	 * @param markup
+	 *            The inherited markup
+	 * @param baseMarkup
+	 *            The base markup
+	 * @param extendIndex
+	 *            Index where <wicket:extend> has been found
+	 */
+	private void merge(final Markup markup, final Markup baseMarkup, int extendIndex)
+	{
+		// True if either <wicket:head> or <head> has been processed
+		boolean wicketHeadProcessed = false;
+
+		// Add all elements from the base markup to the new list
+		// until <wicket:child/> is found. Convert <wicket:child/>
+		// into <wicket:child> and add it as well.
+		WicketTag childTag = null;
+		int baseIndex = 0;
+		for (; baseIndex < baseMarkup.size(); baseIndex++)
+		{
+			MarkupElement element = baseMarkup.get(baseIndex);
+			if (element instanceof RawMarkup)
+			{
+				// Add the element to the merged list
+				addMarkupElement(element);
+				continue;
+			}
+
+			final ComponentTag tag = (ComponentTag)element;
+
+			// Make sure all tags of the base markup remember where they are
+			// from
+			if ((baseMarkup.getMarkupResourceData().getResource() != null) && (tag.getMarkupClass() == null))
+			{
+				tag.setMarkupClass(baseMarkup.getMarkupResourceData().getResource().getMarkupClass());
+			}
+
+			if (element instanceof WicketTag)
+			{
+				WicketTag wtag = (WicketTag)element;
+
+				// Found org.apache.wicket.child in base markup. In case of 3+ level
+				// inheritance make sure the child tag is not from one of the
+				// deeper levels
+				if (wtag.isChildTag()
+						&& (tag.getMarkupClass() == baseMarkup.getMarkupResourceData().getResource().getMarkupClass()))
+				{
+					if (wtag.isOpenClose())
+					{
+						// <wicket:child /> => <wicket:child>...</wicket:child>
+						childTag = wtag;
+						WicketTag childOpenTag = (WicketTag)wtag.mutable();
+						childOpenTag.getXmlTag().setType(XmlTag.OPEN);
+						childOpenTag.setMarkupClass(baseMarkup.getMarkupResourceData().getResource().getMarkupClass());
+						addMarkupElement(childOpenTag);
+						break;
+					}
+					else if (wtag.isOpen())
+					{
+						// <wicket:child>
+						addMarkupElement(wtag);
+						break;
+					}
+					else
+					{
+						throw new WicketRuntimeException("Did not expect a </wicket:child> tag in "
+								+ baseMarkup.toString());
+					}
+				}
+
+				// Process the head of the extended markup only once
+				if (wicketHeadProcessed == false)
+				{
+					// if </wicket:head> in base markup
+					if (wtag.isClose() && wtag.isHeadTag())
+					{
+						wicketHeadProcessed = true;
+
+						// Add the current close tag
+						addMarkupElement(wtag);
+
+						// Add the <wicket:head> body from the derived markup.
+						copyWicketHead(markup, extendIndex);
+
+						// Do not add the current tag. It has already been
+						// added.
+						continue;
+					}
+
+					// if <wicket:panel> or ... in base markup
+					if (wtag.isOpen() && wtag.isMajorWicketComponentTag())
+					{
+						wicketHeadProcessed = true;
+
+						// Add the <wicket:head> body from the derived markup.
+						copyWicketHead(markup, extendIndex);
+					}
+				}
+			}
+
+			// Process the head of the extended markup only once
+			if (wicketHeadProcessed == false)
+			{
+				// if <head> in base markup
+				if ((tag.isClose() && TagUtils.isHeadTag(tag))
+						|| (tag.isOpen() && TagUtils.isBodyTag(tag)))
+				{
+					wicketHeadProcessed = true;
+
+					// Add the <wicket:head> body from the derived markup.
+					copyWicketHead(markup, extendIndex);
+				}
+			}
+
+			// Add the element to the merged list
+			addMarkupElement(element);
+		}
+
+		if (baseIndex == baseMarkup.size())
+		{
+			throw new WicketRuntimeException("Expected to find <wicket:child/> in base markup: "
+					+ baseMarkup.toString());
+		}
+
+		// Now append all elements from the derived markup starting with
+		// <wicket:extend> until </wicket:extend> to the list
+		for (; extendIndex < markup.size(); extendIndex++)
+		{
+			MarkupElement element = markup.get(extendIndex);
+			addMarkupElement(element);
+
+			if (element instanceof WicketTag)
+			{
+				WicketTag wtag = (WicketTag)element;
+				if (wtag.isExtendTag() && wtag.isClose())
+				{
+					break;
+				}
+			}
+		}
+
+		if (extendIndex == markup.size())
+		{
+			throw new WicketRuntimeException(
+					"Missing close tag </wicket:extend> in derived markup: " + markup.toString());
+		}
+
+		// If <wicket:child> than skip the body and find </wicket:child>
+		if (((ComponentTag)baseMarkup.get(baseIndex)).isOpen())
+		{
+			for (baseIndex++; baseIndex < baseMarkup.size(); baseIndex++)
+			{
+				MarkupElement element = baseMarkup.get(baseIndex);
+				if (element instanceof WicketTag)
+				{
+					WicketTag tag = (WicketTag)element;
+					if (tag.isChildTag() && tag.isClose())
+					{
+						// Ok, skipped the childs content
+						tag.setMarkupClass(baseMarkup.getMarkupResourceData().getResource().getMarkupClass());
+						addMarkupElement(tag);
+						break;
+					}
+					else
+					{
+						throw new WicketRuntimeException(
+								"Wicket tags like <wicket:xxx> are not allowed in between <wicket:child> and </wicket:child> tags: "
+										+ markup.toString());
+					}
+				}
+				else if (element instanceof ComponentTag)
+				{
+					throw new WicketRuntimeException(
+							"Wicket tags identified by wicket:id are not allowed in between <wicket:child> and </wicket:child> tags: "
+									+ markup.toString());
+				}
+			}
+
+			// </wicket:child> not found
+			if (baseIndex == baseMarkup.size())
+			{
+				throw new WicketRuntimeException(
+						"Expected to find </wicket:child> in base markup: " + baseMarkup.toString());
+			}
+		}
+		else
+		{
+			// And now all remaining elements from the derived markup.
+			// But first add </wicket:child>
+			WicketTag childCloseTag = (WicketTag)childTag.mutable();
+			childCloseTag.getXmlTag().setType(XmlTag.CLOSE);
+			childCloseTag.setMarkupClass(baseMarkup.getMarkupResourceData().getResource().getMarkupClass());
+			addMarkupElement(childCloseTag);
+		}
+		
+		for (baseIndex++; baseIndex < baseMarkup.size(); baseIndex++)
+		{
+			MarkupElement element = baseMarkup.get(baseIndex);
+			addMarkupElement(element);
+
+			// Make sure all tags of the base markup remember where they are
+			// from
+			if ((element instanceof ComponentTag) && (baseMarkup.getMarkupResourceData().getResource() != null))
+			{
+				ComponentTag tag = (ComponentTag)element;
+				tag.setMarkupClass(baseMarkup.getMarkupResourceData().getResource().getMarkupClass());
+			}
+		}
+
+		// Automatically add <head> if missing and required. On a Page
+		// it must enclose ALL of the <wicket:head> tags.
+		// Note: HtmlHeaderSectionHandler does something similar, but because
+		// markup filters are not called for merged markup again, ...
+		if (Page.class.isAssignableFrom(markup.getMarkupResourceData().getResource().getMarkupClass()))
+		{
+			// Find the position inside the markup for first <wicket:head>,
+			// last </wicket:head> and <head>
+			int hasOpenWicketHead = -1;
+			int hasCloseWicketHead = -1;
+			int hasHead = -1;
+			for (int i = 0; i < size(); i++)
+			{
+				MarkupElement element = get(i);
+
+				if ((hasOpenWicketHead == -1) && (element instanceof WicketTag)
+						&& ((WicketTag)element).isHeadTag())
+				{
+					hasOpenWicketHead = i;
+				}
+				else if ((element instanceof WicketTag) && ((WicketTag)element).isHeadTag()
+						&& ((WicketTag)element).isClose())
+				{
+					hasCloseWicketHead = i;
+				}
+				else if ((hasHead == -1) && (element instanceof ComponentTag)
+						&& TagUtils.isHeadTag((ComponentTag)element))
+				{
+					hasHead = i;
+				}
+				else if ((hasHead != -1) && (hasOpenWicketHead != -1))
+				{
+					break;
+				}
+			}
+
+			// If a <head> tag is missing, insert it automatically
+			if ((hasOpenWicketHead != -1) && (hasHead == -1))
+			{
+				final XmlTag headOpenTag = new XmlTag();
+				headOpenTag.setName("head");
+				headOpenTag.setType(XmlTag.OPEN);
+				final ComponentTag openTag = new ComponentTag(headOpenTag);
+				openTag.setId(HtmlHeaderSectionHandler.HEADER_ID);
+
+				final XmlTag headCloseTag = new XmlTag();
+				headCloseTag.setName(headOpenTag.getName());
+				headCloseTag.setType(XmlTag.CLOSE);
+				final ComponentTag closeTag = new ComponentTag(headCloseTag);
+				closeTag.setOpenTag(openTag);
+				closeTag.setId(HtmlHeaderSectionHandler.HEADER_ID);
+
+				addMarkupElement(hasOpenWicketHead, openTag);
+				addMarkupElement(hasCloseWicketHead + 2, closeTag);
+			}
+		}
+	}
+
+	/**
+	 * Append the wicket:head regions from the extended markup to the current
+	 * markup
+	 * 
+	 * @param markup
+	 * @param extendIndex
+	 */
+	private void copyWicketHead(final Markup markup, int extendIndex)
+	{
+		boolean copy = false;
+		for (int i = 0; i < extendIndex; i++)
+		{
+			MarkupElement elem = markup.get(i);
+			if (elem instanceof WicketTag)
+			{
+				WicketTag etag = (WicketTag)elem;
+				if (etag.isHeadTag())
+				{
+					if (etag.isOpen())
+					{
+						copy = true;
+					}
+					else
+					{
+						addMarkupElement(elem);
+						break;
+					}
+				}
+			}
+
+			if (copy == true)
+			{
+				addMarkupElement(elem);
+			}
+		}
+	}
+}