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.filter; 018 019import java.text.ParseException; 020import java.util.HashSet; 021import java.util.Locale; 022import java.util.Set; 023 024import org.apache.wicket.markup.ComponentTag; 025import org.apache.wicket.markup.MarkupElement; 026import org.apache.wicket.markup.MarkupResourceStream; 027import org.apache.wicket.markup.WicketParseException; 028import org.apache.wicket.markup.WicketTag; 029import org.apache.wicket.markup.html.border.Border; 030import org.apache.wicket.markup.html.form.AutoLabelTextResolver; 031import org.apache.wicket.markup.html.panel.Panel; 032import org.apache.wicket.markup.parser.AbstractMarkupFilter; 033import org.apache.wicket.markup.resolver.HtmlHeaderResolver; 034import org.apache.wicket.markup.resolver.WicketContainerResolver; 035import org.apache.wicket.markup.resolver.WicketMessageResolver; 036import org.apache.wicket.util.string.Strings; 037 038 039/** 040 * This is a markup inline filter. It identifies xml tags which have a special meaning for Wicket. 041 * There are two type of tags which have a special meaning for Wicket. 042 * <p> 043 * <ul> 044 * <li>All tags with Wicket namespace, e.g. <wicket:remove></li> 045 * <li>All tags with an attribute like wicket:id="myLabel"</li> 046 * </ul> 047 * 048 * @author Juergen Donnerstag 049 */ 050public final class WicketTagIdentifier extends AbstractMarkupFilter 051{ 052 public static final String CONTAINER_INFO = "containerInfo"; 053 /** List of well known wicket tag names */ 054 private static final Set<String> WELL_KNOWN_TAG_NAMES = new HashSet<>(); 055 /** List of raw wicket tag names */ 056 private static final Set<String> RAW_TAG_NAMES = new HashSet<>(); 057 058 public static final String CHILD = "child"; 059 060 public static final String EXTEND = "extend"; 061 public static final String FRAGMENT = "fragment"; 062 public static final String MARKUP_CACHE_KEY = "markupCacheKey"; 063 064 static { 065 WELL_KNOWN_TAG_NAMES.add(Border.BORDER); 066 WELL_KNOWN_TAG_NAMES.add(Border.BODY); 067 WELL_KNOWN_TAG_NAMES.add(AutoLabelTextResolver.LABEL); 068 WELL_KNOWN_TAG_NAMES.add(Panel.PANEL); 069 WELL_KNOWN_TAG_NAMES.add(EnclosureHandler.ENCLOSURE); 070 WELL_KNOWN_TAG_NAMES.add(WicketLinkTagHandler.LINK); 071 WELL_KNOWN_TAG_NAMES.add(WicketRemoveTagHandler.REMOVE); 072 WELL_KNOWN_TAG_NAMES.add(WicketTagIdentifier.FRAGMENT); 073 WELL_KNOWN_TAG_NAMES.add(HtmlHeaderResolver.HEAD); 074 WELL_KNOWN_TAG_NAMES.add(HtmlHeaderResolver.HEADER_ITEMS); 075 WELL_KNOWN_TAG_NAMES.add(WicketTagIdentifier.CHILD); 076 WELL_KNOWN_TAG_NAMES.add(WicketTagIdentifier.EXTEND); 077 WELL_KNOWN_TAG_NAMES.add(WicketContainerResolver.CONTAINER); 078 WELL_KNOWN_TAG_NAMES.add(WicketMessageResolver.MESSAGE); 079 } 080 081 static { 082 RAW_TAG_NAMES.add(WicketTagIdentifier.CHILD); 083 RAW_TAG_NAMES.add(WicketTagIdentifier.EXTEND); 084 } 085 086 /** 087 * Construct. 088 * 089 * @param markup 090 * The markup as known by now 091 */ 092 public WicketTagIdentifier(final MarkupResourceStream markup) 093 { 094 super(markup); 095 } 096 097 /** 098 * Get the next tag from the next MarkupFilter in the chain and search for Wicket specific tags. 099 * <p> 100 * Note: The xml parser - the next MarkupFilter in the chain - returns XmlTags which are a 101 * subclass of MarkupElement. The implementation of this filter will return either ComponentTag 102 * or WicketTag. Both are subclasses of MarkupElement and both maintain a 103 * reference to the XmlTag. But no XmlTag is returned. 104 * 105 * @see org.apache.wicket.markup.parser.IMarkupFilter#nextElement() 106 * @return The next tag from markup to be processed. If {@code null} then no more tags are available 107 */ 108 @Override 109 protected MarkupElement onComponentTag(ComponentTag tag) throws ParseException 110 { 111 final String namespace = getWicketNamespace(); 112 113 // If the form <tag wicket:id = "value"> is used 114 final String wicketIdValue = tag.getAttributes().getString(namespace + ":id"); 115 116 // Identify tags with Wicket namespace 117 if (namespace.equalsIgnoreCase(tag.getNamespace())) 118 { 119 // It is <wicket:...> 120 tag = new WicketTag(tag.getXmlTag()); 121 122 if (Strings.isEmpty(wicketIdValue)) 123 { 124 // Make it a Wicket component. 125 tag.setId(namespace + "_" + tag.getName() + getRequestUniqueId()); 126 tag.setUserData(CONTAINER_INFO, getMarkupResourceStream().getContainerInfo()); 127 tag.setUserData(MARKUP_CACHE_KEY, getMarkupResourceStream().getCacheKey()); 128 tag.setModified(true); 129 130 if (isRaw(tag)) 131 { 132 tag.setFlag(ComponentTag.RENDER_RAW, true); 133 } 134 else if (tag.isClose() == false) 135 { 136 tag.setAutoComponentTag(true); 137 } 138 } 139 140 // If the tag is not a well-known wicket namespace tag 141 if (!isWellKnown(tag)) 142 { 143 // give up 144 throw new WicketParseException("Unknown tag name with Wicket namespace: '" + 145 tag.getName() + "'. Might be you haven't installed the appropriate resolver?", 146 tag); 147 } 148 } 149 150 if (wicketIdValue != null) 151 { 152 if (wicketIdValue.trim().length() == 0) 153 { 154 throw new WicketParseException( 155 "The wicket:id attribute value must not be empty. May be unmatched quotes?!?", 156 tag); 157 } 158 // Make it a wicket component. Otherwise it would be RawMarkup 159 tag.setId(wicketIdValue); 160 } 161 162 return tag; 163 } 164 165 /** 166 * Register a new well known wicket tag name (e.g. panel) 167 * 168 * @param name 169 */ 170 public static void registerWellKnownTagName(final String name) 171 { 172 String lowerCaseName = name.toLowerCase(Locale.ROOT); 173 WELL_KNOWN_TAG_NAMES.add(lowerCaseName); 174 } 175 176 /** 177 * 178 * @param tag 179 * @return true, if name is known 180 */ 181 private boolean isWellKnown(final ComponentTag tag) 182 { 183 String lowerCaseTagName = tag.getName().toLowerCase(Locale.ROOT); 184 return WELL_KNOWN_TAG_NAMES.contains(lowerCaseTagName); 185 } 186 187 /** 188 * 189 * @param tag 190 * @return true, if tag must be raw 191 */ 192 private boolean isRaw(final ComponentTag tag) 193 { 194 String lowerCaseTagName = tag.getName().toLowerCase(Locale.ROOT); 195 return RAW_TAG_NAMES.contains(lowerCaseTagName); 196 } 197}