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.head; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022import java.util.Map; 023import java.util.Objects; 024 025import org.apache.wicket.model.IModel; 026import org.apache.wicket.model.Model; 027import org.apache.wicket.request.Response; 028import org.apache.wicket.util.lang.Args; 029import org.apache.wicket.util.string.Strings; 030import org.apache.wicket.util.value.ValueMap; 031 032/** 033 * {@link HeaderItem} for meta information such as <meta> tags or 034 * canonical <link> 035 * 036 * @author andrea del bene 037 * @since 6.17.0 038 */ 039public class MetaDataHeaderItem extends HeaderItem 040{ 041 private static final long serialVersionUID = 1L; 042 043 /** 044 * The meta tag name 045 */ 046 public static final String META_TAG = "meta"; 047 /** 048 * the link tag name 049 */ 050 public static final String LINK_TAG = "link"; 051 052 private final Map<String, Object> tagAttributes; 053 private final List<String> tagMinimizedAttributes; 054 private final String tagName; 055 056 /** 057 * Build a new {@link MetaDataHeaderItem} having {@code tagName} as tag. 058 * 059 * @param tagName 060 * the name of the tag 061 */ 062 public MetaDataHeaderItem(String tagName) 063 { 064 this.tagName = Args.notEmpty(tagName, "tagName"); 065 this.tagAttributes = new ValueMap(); 066 this.tagMinimizedAttributes = new ArrayList<>(); 067 } 068 069 /** 070 * Add a tag attribute to the item. If the attribute value is a {@link IModel}, 071 * the object wrapped inside the model is used as actual value. 072 * 073 * @param attributeName 074 * the attribute name 075 * @param attributeValue 076 * the attribute value 077 * @return 078 * The current item. 079 */ 080 public MetaDataHeaderItem addTagAttribute(String attributeName, Object attributeValue) 081 { 082 Args.notEmpty(attributeName, "attributeName"); 083 Args.notNull(attributeValue, "attributeValue"); 084 085 tagAttributes.put(attributeName, attributeValue); 086 return this; 087 } 088 089 /** 090 * Add a minimized tag attribute to the item. The attribute has no value and 091 * only its name is rendered (for example 'async') 092 * 093 * @param attributeName 094 * the attribute name 095 * @return 096 * The current item. 097 */ 098 public MetaDataHeaderItem addTagAttribute(String attributeName) 099 { 100 Args.notEmpty(attributeName, "attributeName"); 101 102 tagMinimizedAttributes.add(attributeName); 103 return this; 104 } 105 106 /** 107 * Generate the string representation for the current item. 108 * 109 * @return 110 * The string representation for the current item. 111 */ 112 public String generateString() 113 { 114 StringBuilder buffer = new StringBuilder(); 115 116 buffer.append('<').append(tagName); 117 118 for (Map.Entry<String, Object> entry : tagAttributes.entrySet()) 119 { 120 Object value = entry.getValue(); 121 122 if (value instanceof IModel) 123 { 124 value = ((IModel<?>)value).getObject(); 125 } 126 127 buffer.append(' ') 128 .append(Strings.escapeMarkup(entry.getKey())); 129 130 if (value != null) 131 { 132 buffer.append('=') 133 .append('"') 134 .append(Strings.replaceAll(value.toString(), "\"", "\\\"")) 135 .append('"'); 136 } 137 } 138 139 for (String attrName : tagMinimizedAttributes) 140 { 141 buffer.append(' ') 142 .append(Strings.escapeMarkup(attrName)); 143 } 144 145 buffer.append(" />\n"); 146 147 return buffer.toString(); 148 } 149 150 @Override 151 public Iterable<?> getRenderTokens() 152 { 153 return Collections.singletonList(generateString()); 154 } 155 156 @Override 157 public void render(Response response) 158 { 159 response.write(generateString()); 160 } 161 162 /** 163 * Factory method to create <meta> tag. 164 * 165 * @param httpEquiv 166 * the 'httpEquiv' attribute of the tag 167 * @param content 168 * the 'content' attribute of the tag 169 * @return 170 * A new {@link MetaDataHeaderItem} 171 */ 172 public static MetaDataHeaderItem forHttpEquiv(String httpEquiv, String content) 173 { 174 return forHttpEquiv(Model.of(httpEquiv), Model.of(content)); 175 } 176 177 /** 178 * Factory method to create <meta> tag. 179 * 180 * @param httpEquiv 181 * the 'httpEquiv' attribute of the tag 182 * @param content 183 * the 'content' attribute of the tag 184 * @return 185 * A new {@link MetaDataHeaderItem} 186 */ 187 public static MetaDataHeaderItem forHttpEquiv(IModel<String> httpEquiv, IModel<String> content) 188 { 189 MetaDataHeaderItem headerItem = new MetaDataHeaderItem(META_TAG); 190 191 headerItem.addTagAttribute("http-equiv", httpEquiv); 192 headerItem.addTagAttribute("content", content); 193 194 return headerItem; 195 } 196 197 /** 198 * Factory method to create <meta> tag. 199 * 200 * @param name 201 * the 'name' attribute of the tag 202 * @param content 203 * the 'content' attribute of the tag 204 * @return 205 * A new {@link MetaDataHeaderItem} 206 */ 207 public static MetaDataHeaderItem forMetaTag(String name, String content) 208 { 209 return forMetaTag(Model.of(name), Model.of(content)); 210 } 211 212 /** 213 * Factory method to create <meta> tag. 214 * 215 * @param name 216 * the 'name' attribute of the tag as String model 217 * @param content 218 * the 'content' attribute of the tag as String model 219 * @return 220 * A new {@link MetaDataHeaderItem} 221 */ 222 public static MetaDataHeaderItem forMetaTag(IModel<String> name, IModel<String> content) 223 { 224 MetaDataHeaderItem headerItem = new MetaDataHeaderItem(META_TAG); 225 226 headerItem.addTagAttribute("name", name); 227 headerItem.addTagAttribute("content", content); 228 229 return headerItem; 230 } 231 232 /** 233 * Factory method to create <link> tag. 234 * 235 * @param rel 236 * the 'rel' attribute of the tag 237 * @param href 238 * the 'href' attribute of the tag 239 * @return 240 * A new {@link MetaDataHeaderItem} 241 */ 242 public static MetaDataHeaderItem forLinkTag(String rel, String href) 243 { 244 return forLinkTag(Model.of(rel), Model.of(href)); 245 } 246 247 /** 248 * Factory method to create <link> tag. 249 * 250 * @param rel 251 * the 'rel' attribute of the tag as String model 252 * @param href 253 * the 'href' attribute of the tag as String model 254 * @return 255 * A new {@link MetaDataHeaderItem} 256 */ 257 public static MetaDataHeaderItem forLinkTag(IModel<String> rel, IModel<String> href) 258 { 259 MetaDataHeaderItem headerItem = new MetaDataHeaderItem(LINK_TAG); 260 261 headerItem.addTagAttribute("rel", rel); 262 headerItem.addTagAttribute("href", href); 263 264 return headerItem; 265 } 266 267 @Override 268 public boolean equals(Object o) 269 { 270 if (this == o) return true; 271 if (o == null || getClass() != o.getClass()) return false; 272 MetaDataHeaderItem that = (MetaDataHeaderItem) o; 273 return Objects.equals(tagAttributes, that.tagAttributes) && 274 Objects.equals(tagMinimizedAttributes, that.tagMinimizedAttributes) && 275 Objects.equals(tagName, that.tagName); 276 } 277 278 @Override 279 public int hashCode() 280 { 281 // Not using `Objects.hash` for performance reasons 282 int result = tagAttributes != null ? tagAttributes.hashCode() : 0; 283 result = 31 * result + (tagMinimizedAttributes != null ? tagMinimizedAttributes.hashCode() : 0); 284 result = 31 * result + (tagName != null ? tagName.hashCode() : 0); 285 return result; 286 } 287}