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.util.template; 018 019import java.io.IOException; 020import java.util.Locale; 021import java.util.Map; 022import java.util.Objects; 023 024import org.apache.wicket.Application; 025import org.apache.wicket.util.io.Streams; 026import org.apache.wicket.util.lang.Packages; 027import org.apache.wicket.util.resource.IResourceStream; 028import org.apache.wicket.util.resource.ResourceStreamNotFoundException; 029import org.apache.wicket.core.util.resource.locator.ResourceStreamLocator; 030import org.apache.wicket.util.string.interpolator.MapVariableInterpolator; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034 035/** 036 * A <code>String</code> resource that can be appended to. 037 * 038 * @author Eelco Hillenius 039 * @since 1.2.6 040 */ 041public class PackageTextTemplate extends TextTemplate 042{ 043 /** log. */ 044 private static final Logger log = LoggerFactory.getLogger(PackageTextTemplate.class); 045 046 private static final long serialVersionUID = 1L; 047 048 /** The content type used if not provided in the constructor */ 049 public static final String DEFAULT_CONTENT_TYPE = "text"; 050 051 /** The encoding used if not provided in the constructor */ 052 public static final String DEFAULT_ENCODING = null; 053 054 /** contents */ 055 private final StringBuilder buffer = new StringBuilder(); 056 057 private final Class<?> scope; 058 059 private final String fileName; 060 061 private String encoding; 062 063 /** 064 * Constructor. 065 * 066 * @param clazz 067 * the <code>Class</code> to be used for retrieving the classloader for loading the 068 * <code>PackagedTextTemplate</code> 069 * @param fileName 070 * the name of the file, relative to the <code>clazz</code> position 071 */ 072 public PackageTextTemplate(final Class<?> clazz, final String fileName) 073 { 074 this(clazz, fileName, DEFAULT_CONTENT_TYPE); 075 } 076 077 /** 078 * Constructor. 079 * 080 * @param clazz 081 * the <code>Class</code> to be used for retrieving the classloader for loading the 082 * <code>PackagedTextTemplate</code> 083 * @param fileName 084 * the name of the file, relative to the <code>clazz</code> position 085 * @param contentType 086 * the mime type of this resource, such as "<code>image/jpeg</code>" or " 087 * <code>text/html</code>" 088 */ 089 public PackageTextTemplate(final Class<?> clazz, final String fileName, final String contentType) 090 { 091 this(clazz, fileName, contentType, DEFAULT_ENCODING); 092 } 093 094 /** 095 * Constructor. 096 * 097 * @param clazz 098 * the <code>Class</code> to be used for retrieving the classloader for loading the 099 * <code>PackagedTextTemplate</code> 100 * @param fileName 101 * the name of the file, relative to the <code>clazz</code> position 102 * @param contentType 103 * the mime type of this resource, such as "<code>image/jpeg</code>" or " 104 * <code>text/html</code>" 105 * @param encoding 106 * the file's encoding, for example, "<code>UTF-8</code>" 107 */ 108 public PackageTextTemplate(final Class<?> clazz, final String fileName, 109 final String contentType, final String encoding) 110 { 111 this(clazz, fileName, null, null, null, contentType, encoding); 112 } 113 114 /** 115 * Constructor. 116 * 117 * @param clazz 118 * the <code>Class</code> to be used for retrieving the classloader for loading the 119 * <code>PackagedTextTemplate</code> 120 * @param fileName 121 * the name of the file, relative to the <code>clazz</code> position 122 * @param style 123 * Any resource style, such as a skin style (see {@link org.apache.wicket.Session}) 124 * @param variation 125 * The template's variation (of the style) 126 * @param locale 127 * The locale of the resource to load 128 * @param contentType 129 * the mime type of this resource, such as "<code>image/jpeg</code>" or " 130 * <code>text/html</code>" 131 * @param encoding 132 * the file's encoding, for example, "<code>UTF-8</code>" 133 */ 134 public PackageTextTemplate(final Class<?> clazz, final String fileName, final String style, final String variation, 135 final Locale locale, final String contentType, final String encoding) 136 { 137 super(contentType); 138 139 this.scope = clazz; 140 this.fileName = fileName; 141 this.encoding = encoding; 142 143 setStyle(style); 144 setVariation(variation); 145 setLocale(locale); 146 } 147 148 @Override 149 public void setStyle(String style) 150 { 151 if (Objects.equals(style, getStyle()) == false) 152 { 153 buffer.setLength(0); 154 } 155 super.setStyle(style); 156 } 157 158 @Override 159 public void setLocale(Locale locale) 160 { 161 if (Objects.equals(locale, getLocale()) == false) 162 { 163 buffer.setLength(0); 164 } 165 super.setLocale(locale); 166 } 167 168 @Override 169 public void setVariation(String variation) 170 { 171 if (Objects.equals(variation, getVariation()) == false) 172 { 173 buffer.setLength(0); 174 } 175 super.setVariation(variation); 176 } 177 178 public void setEncoding(String encoding) 179 { 180 if (Objects.equals(encoding, this.encoding) == false) 181 { 182 buffer.setLength(0); 183 } 184 this.encoding = encoding == null ? DEFAULT_ENCODING : encoding; 185 } 186 187 /** 188 * Loads the template if it is not loaded yet 189 */ 190 private void load() { 191 if (buffer.length() == 0) 192 { 193 String path = Packages.absolutePath(scope, fileName); 194 195 Application app = Application.get(); 196 197 // first try default class loading locator to find the resource 198 IResourceStream stream = app.getResourceSettings() 199 .getResourceStreamLocator() 200 .locate(scope, path, getStyle(), getVariation(), getLocale(), null, false); 201 202 if (stream == null) 203 { 204 // if the default locator didn't find the resource then fallback 205 stream = new ResourceStreamLocator().locate(scope, path, getStyle(), getVariation(), getLocale(), null, false); 206 } 207 208 if (stream == null) 209 { 210 throw new IllegalArgumentException("resource " + fileName + " not found for scope " + 211 scope + " (path = " + path + ")"); 212 } 213 214 setLastModified(stream.lastModifiedTime()); 215 216 try 217 { 218 if (encoding != null) 219 { 220 buffer.append(Streams.readString(stream.getInputStream(), encoding)); 221 } 222 else 223 { 224 buffer.append(Streams.readString(stream.getInputStream())); 225 } 226 } 227 catch (IOException e) 228 { 229 throw new RuntimeException(e); 230 } 231 catch (ResourceStreamNotFoundException e) 232 { 233 throw new RuntimeException(e); 234 } 235 finally 236 { 237 try 238 { 239 stream.close(); 240 } 241 catch (IOException e) 242 { 243 log.error(e.getMessage(), e); 244 } 245 } 246 } 247 } 248 249 /** 250 * @see org.apache.wicket.util.resource.AbstractStringResourceStream#getString() 251 */ 252 @Override 253 public String getString() 254 { 255 load(); 256 return buffer.toString(); 257 } 258 259 /** 260 * Interpolates a <code>Map</code> of variables with the content and replaces the content with 261 * the result. Variables are denoted in the <code>String</code> by the 262 * <code>syntax ${variableName}</code>. The contents will be altered by replacing each variable 263 * of the form <code>${variableName}</code> with the value returned by 264 * <code>variables.getValue("variableName")</code>. 265 * <p> 266 * WARNING: there is no going back to the original contents after the interpolation is done. If 267 * you need to do different interpolations on the same original contents, use the method 268 * {@link #asString(Map)} instead. 269 * </p> 270 * 271 * @param variables 272 * a <code>Map</code> of variables to interpolate 273 * @return this for chaining 274 */ 275 @Override 276 public final TextTemplate interpolate(Map<String, ?> variables) 277 { 278 if (variables != null) 279 { 280 load(); 281 String result = new MapVariableInterpolator(buffer.toString(), variables).toString(); 282 buffer.setLength(0); 283 buffer.append(result); 284 } 285 return this; 286 } 287 288 289}