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.parser; 018 019import java.text.ParseException; 020import java.util.HashMap; 021import java.util.Map; 022import java.util.concurrent.atomic.AtomicInteger; 023 024import org.apache.wicket.MetaDataKey; 025import org.apache.wicket.markup.ComponentTag; 026import org.apache.wicket.markup.ContainerInfo; 027import org.apache.wicket.markup.HtmlSpecialTag; 028import org.apache.wicket.markup.Markup; 029import org.apache.wicket.markup.MarkupElement; 030import org.apache.wicket.markup.MarkupParser; 031import org.apache.wicket.markup.MarkupResourceStream; 032import org.apache.wicket.markup.MarkupStream; 033import org.apache.wicket.request.cycle.RequestCycle; 034 035 036/** 037 * Base class for markup filters 038 * 039 * @author Jonathan Locke 040 * @author Juergen Donnerstag 041 */ 042public abstract class AbstractMarkupFilter implements IMarkupFilter 043{ 044 /** The markup created by reading the markup file */ 045 private final MarkupResourceStream markupResourceStream; 046 047 /** The next MarkupFilter in the chain */ 048 private IMarkupFilter parent; 049 050 /** 051 * A key for a request-relative map of counters. 052 * As map keys we use the class name of the {@link org.apache.wicket.markup.MarkupResourceStream}'s owner 053 * container (see {@link org.apache.wicket.markup.MarkupResourceStream#getContainerInfo()}), 054 * meaning that each container has its own counter. 055 * The counters are used by {@link #getRequestUniqueId()} to get unique ids for markup tags. 056 * **/ 057 protected final static MetaDataKey<Map<String, AtomicInteger>> REQUEST_COUNTER_KEY = new MetaDataKey<>() 058 { 059 private static final long serialVersionUID = 1L; 060 }; 061 062 /** 063 * Construct. 064 */ 065 public AbstractMarkupFilter() 066 { 067 this(null); 068 } 069 070 public AbstractMarkupFilter(final MarkupResourceStream markupResourceStream) 071 { 072 this.markupResourceStream = markupResourceStream; 073 } 074 075 076 /** 077 * @return The next MarkupFilter in the chain 078 */ 079 @Override 080 public IMarkupFilter getNextFilter() 081 { 082 return parent; 083 } 084 085 /** 086 * Set new parent. 087 * 088 * @param parent 089 * The parent of this component The next element in the chain 090 */ 091 @Override 092 public void setNextFilter(final IMarkupFilter parent) 093 { 094 this.parent = parent; 095 } 096 097 /** 098 * Get the next xml element from the markup. If eof, than return null. Ignore raw markup. Invoke 099 * nextTag(tag) if a tag was found. 100 */ 101 @Override 102 public MarkupElement nextElement() throws ParseException 103 { 104 MarkupElement elem = getNextFilter().nextElement(); 105 if (elem != null) 106 { 107 if (elem instanceof ComponentTag) 108 { 109 elem = onComponentTag((ComponentTag)elem); 110 } 111 else if (elem instanceof HtmlSpecialTag) 112 { 113 elem = onSpecialTag((HtmlSpecialTag)elem); 114 } 115 } 116 return elem; 117 } 118 119 /** 120 * Invoked when a ComponentTag was found. 121 * <p> 122 * By default this method is also called for WicketTags. 123 * 124 * @param tag 125 * @return Usually the same as the tag attribute 126 * @throws ParseException 127 */ 128 protected abstract MarkupElement onComponentTag(ComponentTag tag) throws ParseException; 129 130 /** 131 * Invoked when a WicketTag was found. 132 * 133 * @param tag 134 * @return Usually the same as the tag attribute 135 * @throws ParseException 136 */ 137 138 /** 139 * Invoked when a tags (e.g. DOCTYPE, PROCESSING_INSTRUCTION, etc. which have been identified 140 * as special tags by the xml parser. 141 * 142 * @param tag 143 * @return Usually the same as the tag attribute 144 * @throws ParseException 145 */ 146 protected MarkupElement onSpecialTag(final HtmlSpecialTag tag) throws ParseException 147 { 148 return tag; 149 } 150 151 @Override 152 public void postProcess(final Markup markup) 153 { 154 } 155 156 protected MarkupResourceStream getMarkupResourceStream() { 157 return markupResourceStream; 158 } 159 160 /** 161 * Extracts the markup namespace from the MarkupResourceStream 162 * passed at creation time. 163 * 164 * <p> 165 * There are two versions of this method because most IMarkupFilter's 166 * have dual personality - {@link IMarkupFilter} (one instance per MarkupParser) 167 * and {@link org.apache.wicket.markup.resolver.IComponentResolver} (one 168 * instance per application). 169 * </p> 170 * 171 * @return the namespace of the loaded markup 172 */ 173 protected String getWicketNamespace() 174 { 175 return getWicketNamespace(null); 176 } 177 178 /** 179 * Extracts the markup namespace from the passed MarkupStream if available, 180 * or from the MarkupResourceStream passed at creation time. 181 * 182 * <p> 183 * There are two versions of this method because most IMarkupFilter's 184 * have dual personality - {@link IMarkupFilter} (one instance per MarkupParser) 185 * and {@link org.apache.wicket.markup.resolver.IComponentResolver} (one 186 * instance per application). 187 * </p> 188 * 189 * @param markupStream 190 * the markup stream 191 * @return namespace extracted from the markup 192 */ 193 protected String getWicketNamespace(final MarkupStream markupStream) 194 { 195 String wicketNamespace = MarkupParser.WICKET; 196 if (markupStream != null) 197 { 198 wicketNamespace = markupStream.getWicketNamespace(); 199 } 200 else if (markupResourceStream != null) 201 { 202 wicketNamespace = markupResourceStream.getWicketNamespace(); 203 } 204 return wicketNamespace; 205 } 206 207 /** 208 * Returns an id using the request-relative counter associated with the 209 * underlying {@link org.apache.wicket.markup.MarkupResourceStream}'s owner container 210 * (see {@link org.apache.wicket.markup.MarkupResourceStream#getContainerInfo()}). 211 * This can be useful for autocomponent tags that need to get a tag id. 212 * 213 * @return 214 * the request-relative id 215 */ 216 protected int getRequestUniqueId() 217 { 218 RequestCycle requestCycle = RequestCycle.get(); 219 Map<String, AtomicInteger> markupUniqueCounters = requestCycle.getMetaData(REQUEST_COUNTER_KEY); 220 ContainerInfo containerInfo = getMarkupResourceStream().getContainerInfo(); 221 String cacheKey = containerInfo != null ? containerInfo.getContainerClass().getCanonicalName() : null; 222 223 if (markupUniqueCounters == null) 224 { 225 markupUniqueCounters = new HashMap<>(); 226 227 requestCycle.setMetaData(REQUEST_COUNTER_KEY, markupUniqueCounters); 228 } 229 230 AtomicInteger counter = markupUniqueCounters.get(cacheKey); 231 232 if (counter == null) 233 { 234 counter = new AtomicInteger(); 235 markupUniqueCounters.put(cacheKey, counter); 236 } 237 238 int cacheHash = cacheKey == null ? 0 : cacheKey.hashCode(); 239 240 //add the counter value to the string hash 241 //using the same algorithm of String#hashCode() 242 return cacheHash * 31 + counter.getAndIncrement(); 243 } 244}