/* * 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.parser; import java.text.ParseException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.apache.wicket.MetaDataKey; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.ContainerInfo; import org.apache.wicket.markup.HtmlSpecialTag; import org.apache.wicket.markup.Markup; import org.apache.wicket.markup.MarkupElement; import org.apache.wicket.markup.MarkupParser; import org.apache.wicket.markup.MarkupResourceStream; import org.apache.wicket.markup.MarkupStream; import org.apache.wicket.request.cycle.RequestCycle; /** * Base class for markup filters * * @author Jonathan Locke * @author Juergen Donnerstag */ public abstract class AbstractMarkupFilter implements IMarkupFilter { /** The markup created by reading the markup file */ private final MarkupResourceStream markupResourceStream; /** The next MarkupFilter in the chain */ private IMarkupFilter parent; /** * A key for a request-relative map of counters. * As map keys we use the class name of the {@link org.apache.wicket.markup.MarkupResourceStream}'s owner * container (see {@link org.apache.wicket.markup.MarkupResourceStream#getContainerInfo()}), * meaning that each container has its own counter. * The counters are used by {@link #getRequestUniqueId()} to get unique ids for markup tags. * **/ protected final static MetaDataKey<Map<String, AtomicInteger>> REQUEST_COUNTER_KEY = new MetaDataKey<Map<String, AtomicInteger>>() { private static final long serialVersionUID = 1L; }; /** * Construct. */ public AbstractMarkupFilter() { this(null); } public AbstractMarkupFilter(final MarkupResourceStream markupResourceStream) { this.markupResourceStream = markupResourceStream; } /** * @return The next MarkupFilter in the chain */ @Override public IMarkupFilter getNextFilter() { return parent; } /** * Set new parent. * * @param parent * The parent of this component The next element in the chain */ @Override public void setNextFilter(final IMarkupFilter parent) { this.parent = parent; } /** * Get the next xml element from the markup. If eof, than retun null. Ignore raw markup. Invoke * nextTag(tag) if a tag was found. */ @Override public MarkupElement nextElement() throws ParseException { MarkupElement elem = getNextFilter().nextElement(); if (elem != null) { if (elem instanceof ComponentTag) { elem = onComponentTag((ComponentTag)elem); } else if (elem instanceof HtmlSpecialTag) { elem = onSpecialTag((HtmlSpecialTag)elem); } } return elem; } /** * Invoked when a ComponentTag was found. * <p> * By default this method is also called for WicketTags. * * @param tag * @return Usually the same as the tag attribute * @throws ParseException */ protected abstract MarkupElement onComponentTag(ComponentTag tag) throws ParseException; /** * Invoked when a WicketTag was found. * * @param tag * @return Usually the same as the tag attribute * @throws ParseException */ /** * Invoked when a tags (e.g. DOCTYPE, PROCESSING_INSTRUCTIION, etc. which have been identified * as special tags by the xml parser. * * @param tag * @return Usually the same as the tag attribute * @throws ParseException */ protected MarkupElement onSpecialTag(final HtmlSpecialTag tag) throws ParseException { return tag; } @Override public void postProcess(final Markup markup) { } protected MarkupResourceStream getMarkupResourceStream() { return markupResourceStream; } /** * Extracts the markup namespace from the MarkupResourceStream * passed at creation time. * * <p> * There are two versions of this method because most IMarkupFilter's * have dual personality - {@link IMarkupFilter} (one instance per MarkupParser) * and {@link org.apache.wicket.markup.resolver.IComponentResolver} (one * instance per application). * </p> * * @return the namespace of the loaded markup */ protected String getWicketNamespace() { return getWicketNamespace(null); } /** * Extracts the markup namespace from the passed MarkupStream if available, * or from the MarkupResourceStream passed at creation time. * * <p> * There are two versions of this method because most IMarkupFilter's * have dual personality - {@link IMarkupFilter} (one instance per MarkupParser) * and {@link org.apache.wicket.markup.resolver.IComponentResolver} (one * instance per application). * </p> * * @param markupStream * the markup stream * @return namespace extracted from the markup */ protected String getWicketNamespace(final MarkupStream markupStream) { String wicketNamespace = MarkupParser.WICKET; if (markupStream != null) { wicketNamespace = markupStream.getWicketNamespace(); } else if (markupResourceStream != null) { wicketNamespace = markupResourceStream.getWicketNamespace(); } return wicketNamespace; } /** * Returns an id using the request-relative counter associated with the * underlying {@link org.apache.wicket.markup.MarkupResourceStream}'s owner container * (see {@link org.apache.wicket.markup.MarkupResourceStream#getContainerInfo()}). * This can be useful for autocomponent tags that need to get a tag id. * * @return * the request-relative id */ protected int getRequestUniqueId() { RequestCycle requestCycle = RequestCycle.get(); Map<String, AtomicInteger> markupUniqueCounters = requestCycle.getMetaData(REQUEST_COUNTER_KEY); ContainerInfo containerInfo = getMarkupResourceStream().getContainerInfo(); String cacheKey = containerInfo != null ? containerInfo.getContainerClass().getCanonicalName() : null; if (markupUniqueCounters == null) { markupUniqueCounters = new HashMap<>(); requestCycle.setMetaData(REQUEST_COUNTER_KEY, markupUniqueCounters); } AtomicInteger counter = markupUniqueCounters.get(cacheKey); if (counter == null) { counter = new AtomicInteger(); markupUniqueCounters.put(cacheKey, counter); } int cacheHash = cacheKey == null ? 0 : cacheKey.hashCode(); //add the counter value to the string hash //using the same algorithm of String#hashCode() return cacheHash * 31 + counter.getAndIncrement(); } }