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);
}
}
}