private void merge()

in wicket-core/src/main/java/org/apache/wicket/markup/MergedMarkup.java [124:397]


	private void merge(final IMarkupFragment markup, final IMarkupFragment baseMarkup,
		int extendIndex)
	{
		// True if either <wicket:head> or <head> has been processed
		boolean wicketHeadProcessed = false;

		// True, if <head> was found
		boolean foundHeadTag = 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;
		MarkupResourceStream markupResourceStream = baseMarkup.getMarkupResourceStream();
		IResourceStream resource = markupResourceStream.getResource();
		Class<? extends Component> markupClass = markupResourceStream.getMarkupClass();

		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 (resource != null && tag.getMarkupClass() == null)
			{
				tag.setMarkupClass(markupClass);
			}

			if (element instanceof WicketTag)
			{
				WicketTag wtag = (WicketTag)element;

				// Found wicket:child in the 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() == markupClass)
				{
					if (wtag.isOpenClose())
					{
						// <wicket:child /> => <wicket:child>...</wicket:child>
						childTag = wtag;
						WicketTag childOpenTag = (WicketTag)wtag.mutable();
						childOpenTag.getXmlTag().setType(TagType.OPEN);
						childOpenTag.setMarkupClass(markupClass);
						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 and no <head>
					if (wtag.isClose() && wtag.isHeadTag() && (foundHeadTag == false))
					{
						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)
			{
				// Remember that we found <head> in the base markup
				if (tag.isOpen() && TagUtils.isHeadTag(tag))
				{
					foundHeadTag = true;
				}

				// if <head> in base markup
				if ((tag.isClose() && TagUtils.isHeadTag(tag)) ||
				    (tag.isClose() && TagUtils.isWicketHeaderItemsTag(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(markupClass);
						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(TagType.CLOSE);
			childCloseTag.setMarkupClass(markupClass);
			childCloseTag.setOpenTag(childTag);
			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 && resource != null)
			{
				ComponentTag tag = (ComponentTag)element;
				if (tag.getMarkupClass() == null){
					tag.setMarkupClass(markupClass);
				}
			}
		}

		// 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.getMarkupResourceStream().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);

				boolean isHeadTag = (element instanceof WicketTag) && ((WicketTag) element).isHeadTag();
				if ((hasOpenWicketHead == -1) && isHeadTag)
				{
					hasOpenWicketHead = i;
				}
				else if (isHeadTag && ((ComponentTag)element).isClose())
				{
					hasCloseWicketHead = i;
				}
				else if ((hasHead == -1) && (element instanceof ComponentTag) &&
					TagUtils.isHeadTag(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(TagType.OPEN);
				final ComponentTag openTag = new ComponentTag(headOpenTag);
				openTag.setId(HtmlHeaderSectionHandler.HEADER_ID);
				openTag.setAutoComponentTag(true);

				final XmlTag headCloseTag = new XmlTag();
				headCloseTag.setName(headOpenTag.getName());
				headCloseTag.setType(TagType.CLOSE);
				final ComponentTag closeTag = new ComponentTag(headCloseTag);
				closeTag.setOpenTag(openTag);
				closeTag.setId(HtmlHeaderSectionHandler.HEADER_ID);

				addMarkupElement(hasOpenWicketHead, openTag);
				addMarkupElement(hasCloseWicketHead + 2, closeTag);
			}
		}
	}