001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.markup.html.panel; 018 019import java.util.ArrayList; 020import java.util.Iterator; 021import java.util.List; 022 023import org.apache.wicket.Component; 024import org.apache.wicket.MarkupContainer; 025import org.apache.wicket.markup.ComponentTag; 026import org.apache.wicket.markup.IMarkupFragment; 027import org.apache.wicket.markup.MarkupException; 028import org.apache.wicket.markup.MarkupStream; 029import org.apache.wicket.markup.html.internal.HtmlHeaderContainer; 030import org.apache.wicket.markup.parser.XmlTag.TagType; 031import org.apache.wicket.markup.resolver.IComponentResolver; 032import org.apache.wicket.util.lang.Classes; 033import org.apache.wicket.util.visit.IVisit; 034import org.apache.wicket.util.visit.IVisitor; 035 036/** 037 * Implements boilerplate as needed by many markup sourcing strategies. 038 * 039 * @author Juergen Donnerstag 040 */ 041public abstract class AbstractMarkupSourcingStrategy implements IMarkupSourcingStrategy 042{ 043 /** 044 * Construct. 045 */ 046 public AbstractMarkupSourcingStrategy() 047 { 048 } 049 050 @Override 051 public abstract IMarkupFragment getMarkup(final MarkupContainer container, final Component child); 052 053 /** 054 * If the child has not been directly added to the container, but via a 055 * TransparentWebMarkupContainer, then we are in trouble. In general Wicket iterates over the 056 * markup elements and searches for associated components, not the other way around. Because of 057 * TransparentWebMarkupContainer (or more generally resolvers), there is no "synchronous" search 058 * possible. 059 * 060 * @param container 061 * the parent container. 062 * @param 063 * containerMarkup 064 * the markup of the parent container. 065 * @param child 066 * The component to find the markup for. 067 * @return the markup fragment for the child, or {@code null}. 068 */ 069 protected IMarkupFragment searchMarkupInTransparentResolvers(MarkupContainer container, 070 IMarkupFragment containerMarkup, Component child) 071 { 072 IMarkupFragment childMarkupFound = null; 073 Iterator<Component> childrenIterator = container.iterator(); 074 final List<MarkupContainer> componentResolvers = new ArrayList<>(); 075 076 //collect all "transparent" (i.e. component resolvers) children 077 container.visitChildren(IComponentResolver.class, new IVisitor<MarkupContainer, Void>() 078 { 079 @Override 080 public void component(MarkupContainer child, IVisit<Void> visit) 081 { 082 componentResolvers.add(child); 083 } 084 }); 085 086 while (childrenIterator.hasNext() && childMarkupFound == null) 087 { 088 Component sibling = childrenIterator.next(); 089 090 if (sibling == child || !sibling.isVisible() || !(sibling instanceof MarkupContainer)) 091 { 092 continue; 093 } 094 095 IMarkupFragment siblingMarkup = containerMarkup.find(sibling.getId()); 096 097 if (siblingMarkup != null) 098 { 099 if (sibling instanceof IComponentResolver) 100 { 101 childMarkupFound = searchInNestedTransparentResolvers(containerMarkup, child, componentResolvers); 102 } 103 else 104 { 105 childMarkupFound = searchMarkupInTransparentResolvers((MarkupContainer)sibling, siblingMarkup, child); 106 } 107 } 108 } 109 110 return childMarkupFound; 111 } 112 113 /** 114 * 115 * Search for the markup of a child that might be nested inside 116 * transparent siblings. For example: 117 * 118 * <pre> 119 * <div wicket:id="outerTransparent"> 120 * <div wicket:id="innerTransparent"> 121 * <span wicket:id="childComponent"></span> 122 * </div> 123 * </div> 124 * </pre> 125 * 126 * @param 127 * containerMarkup 128 * the markup of the parent container. 129 * @param child 130 * The component to find the markup for. 131 * @param componentResolvers 132 * the transparent siblings 133 * 134 * @return the markup fragment for the child, or {@code null}. 135 */ 136 protected IMarkupFragment searchInNestedTransparentResolvers(IMarkupFragment containerMarkup, Component child, 137 List<MarkupContainer> componentResolvers) 138 { 139 IMarkupFragment childMarkupFound = null; 140 141 for (MarkupContainer componentResolver : componentResolvers) 142 { 143 IMarkupFragment resolverMarkup = containerMarkup.find(componentResolver.getId()); 144 IMarkupFragment childMarkup = resolverMarkup != null ? resolverMarkup.find(child.getId()) : null; 145 146 if (childMarkup != null) 147 { 148 IComponentResolver resolverContainer = (IComponentResolver)componentResolver; 149 MarkupStream stream = new MarkupStream(childMarkup); 150 ComponentTag tag = stream.getTag(); 151 152 Component resolvedComponent = componentResolver.get(tag.getId()); 153 if (resolvedComponent == null) 154 { 155 resolvedComponent = resolverContainer.resolve(componentResolver, stream, tag); 156 } 157 158 if (child == resolvedComponent) 159 { 160 childMarkupFound = childMarkup; 161 } 162 } 163 else if (resolverMarkup != null) 164 { 165 List<MarkupContainer> otherResolvers = new ArrayList<>(componentResolvers); 166 167 otherResolvers.remove(componentResolver); 168 169 childMarkupFound = searchInNestedTransparentResolvers(resolverMarkup, child, otherResolvers); 170 } 171 172 if (childMarkupFound != null) 173 { 174 break; 175 } 176 } 177 178 return childMarkupFound; 179 } 180 181 /** 182 * Make sure we open up open-close tags to open-body-close 183 */ 184 @Override 185 public void onComponentTag(final Component component, final ComponentTag tag) 186 { 187 if (tag.isOpenClose()) 188 { 189 tag.setType(TagType.OPEN); 190 } 191 } 192 193 /** 194 * Skip the components body which is expected to be raw markup only (no wicket components). It 195 * will be replaced by the associated markup. 196 */ 197 @Override 198 public void onComponentTagBody(final Component component, final MarkupStream markupStream, 199 final ComponentTag openTag) 200 { 201 // Skip the components body. It will be replaced by the associated markup or fragment 202 if (markupStream.getPreviousTag().isOpen()) 203 { 204 markupStream.skipRawMarkup(); 205 if (markupStream.get().closes(openTag) == false) 206 { 207 throw new MarkupException( 208 markupStream, 209 "Close tag not found for tag: " + 210 openTag.toString() + 211 ". For " + 212 Classes.simpleName(component.getClass()) + 213 " Components only raw markup is allow in between the tags but not other Wicket Component." + 214 ". Component: " + component.toString()); 215 } 216 } 217 } 218 219 /** 220 * Empty. Nothing to be added to the response by default. 221 */ 222 @Override 223 public void renderHead(final Component component, HtmlHeaderContainer container) 224 { 225 } 226}