/* * 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.html.panel; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.IMarkupFragment; import org.apache.wicket.markup.MarkupException; import org.apache.wicket.markup.MarkupStream; import org.apache.wicket.markup.html.internal.HtmlHeaderContainer; import org.apache.wicket.markup.parser.XmlTag.TagType; import org.apache.wicket.markup.resolver.IComponentResolver; import org.apache.wicket.util.lang.Classes; import org.apache.wicket.util.visit.IVisit; import org.apache.wicket.util.visit.IVisitor; /** * Implements boilerplate as needed by many markup sourcing strategies. * * @author Juergen Donnerstag */ public abstract class AbstractMarkupSourcingStrategy implements IMarkupSourcingStrategy { /** * Construct. */ public AbstractMarkupSourcingStrategy() { } @Override public abstract IMarkupFragment getMarkup(final MarkupContainer container, final Component child); /** * If the child has not been directly added to the container, but via a * TransparentWebMarkupContainer, then we are in trouble. In general Wicket iterates over the * markup elements and searches for associated components, not the other way around. Because of * TransparentWebMarkupContainer (or more generally resolvers), there is no "synchronous" search * possible. * * @param container * the parent container. * @param * containerMarkup * the markup of the parent container. * @param child * The component to find the markup for. * @return the markup fragment for the child, or {@code null}. */ protected IMarkupFragment searchMarkupInTransparentResolvers(MarkupContainer container, IMarkupFragment containerMarkup, Component child) { IMarkupFragment childMarkupFound = null; Iterator<Component> childrenIterator = container.iterator(); final List<MarkupContainer> componentResolvers = new ArrayList<>(); //collect all "transparent" (i.e. component resolvers) children container.visitChildren(IComponentResolver.class, new IVisitor<MarkupContainer, Void>() { @Override public void component(MarkupContainer child, IVisit<Void> visit) { componentResolvers.add(child); } }); while (childrenIterator.hasNext() && childMarkupFound == null) { Component sibling = childrenIterator.next(); if (sibling == child || !sibling.isVisible() || !(sibling instanceof MarkupContainer)) { continue; } IMarkupFragment siblingMarkup = containerMarkup.find(sibling.getId()); if (siblingMarkup != null) { if (sibling instanceof IComponentResolver) { childMarkupFound = searchInNestedTransparentResolvers(containerMarkup, child, componentResolvers); } else { childMarkupFound = searchMarkupInTransparentResolvers((MarkupContainer)sibling, siblingMarkup, child); } } } return childMarkupFound; } /** * * Search for the markup of a child that might be nested inside * transparent siblings. For example: * * <pre> * <div wicket:id="outerTransparent"> * <div wicket:id="innerTransparent"> * <span wicket:id="childComponent"></span> * </div> * </div> * </pre> * * @param * containerMarkup * the markup of the parent container. * @param child * The component to find the markup for. * @param componentResolvers * the transparent siblings * * @return the markup fragment for the child, or {@code null}. */ protected IMarkupFragment searchInNestedTransparentResolvers(IMarkupFragment containerMarkup, Component child, List<MarkupContainer> componentResolvers) { IMarkupFragment childMarkupFound = null; for (MarkupContainer componentResolver : componentResolvers) { IMarkupFragment resolverMarkup = containerMarkup.find(componentResolver.getId()); IMarkupFragment childMarkup = resolverMarkup != null ? resolverMarkup.find(child.getId()) : null; if (childMarkup != null) { IComponentResolver resolverContainer = (IComponentResolver)componentResolver; MarkupStream stream = new MarkupStream(childMarkup); ComponentTag tag = stream.getTag(); Component resolvedComponent = componentResolver.get(tag.getId()); if (resolvedComponent == null) { resolvedComponent = resolverContainer.resolve(componentResolver, stream, tag); } if (child == resolvedComponent) { childMarkupFound = childMarkup; } } else if (resolverMarkup != null) { List<MarkupContainer> otherResolvers = new ArrayList<>(componentResolvers); otherResolvers.remove(componentResolver); childMarkupFound = searchInNestedTransparentResolvers(resolverMarkup, child, otherResolvers); } if (childMarkupFound != null) { break; } } return childMarkupFound; } /** * Make sure we open up open-close tags to open-body-close */ @Override public void onComponentTag(final Component component, final ComponentTag tag) { if (tag.isOpenClose()) { tag.setType(TagType.OPEN); } } /** * Skip the components body which is expected to be raw markup only (no wicket components). It * will be replaced by the associated markup. */ @Override public void onComponentTagBody(final Component component, final MarkupStream markupStream, final ComponentTag openTag) { // Skip the components body. It will be replaced by the associated markup or fragment if (markupStream.getPreviousTag().isOpen()) { markupStream.skipRawMarkup(); if (markupStream.get().closes(openTag) == false) { throw new MarkupException( markupStream, "Close tag not found for tag: " + openTag.toString() + ". For " + Classes.simpleName(component.getClass()) + " Components only raw markup is allow in between the tags but not other Wicket Component." + ". Component: " + component.toString()); } } } /** * Empty. Nothing to be added to the response by default. */ @Override public void renderHead(final Component component, HtmlHeaderContainer container) { } }