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.resource; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.nio.charset.Charset; 022import java.util.Locale; 023import java.util.regex.Pattern; 024 025import org.apache.wicket.WicketRuntimeException; 026import org.apache.wicket.request.Url; 027import org.apache.wicket.request.resource.ResourceReference; 028import org.apache.wicket.util.io.IOUtils; 029import org.apache.wicket.util.lang.Args; 030import org.apache.wicket.util.resource.IResourceStream; 031import org.apache.wicket.util.resource.ResourceStreamNotFoundException; 032import org.apache.wicket.util.string.Strings; 033 034/** 035 * Utilities for resources. 036 * 037 * @author Jeremy Thomerson 038 */ 039public class ResourceUtil 040{ 041 042 private static final Pattern ESCAPED_ATTRIBUTE_PATTERN = Pattern.compile("(\\w)~(\\w)"); 043 044 /** 045 * Reads resource reference attributes (style, locale, variation) encoded in the given string. 046 * 047 * @param encodedAttributes 048 * the string containing the resource attributes 049 * @return the encoded attributes 050 * 051 * @see ResourceReference.UrlAttributes 052 */ 053 public static ResourceReference.UrlAttributes decodeResourceReferenceAttributes(String encodedAttributes) 054 { 055 Locale locale = null; 056 String style = null; 057 String variation = null; 058 059 if (Strings.isEmpty(encodedAttributes) == false) 060 { 061 String split[] = Strings.split(encodedAttributes, '-'); 062 locale = parseLocale(split[0]); 063 if (split.length == 2) 064 { 065 style = Strings.defaultIfEmpty(unescapeAttributesSeparator(split[1]), null); 066 } 067 else if (split.length == 3) 068 { 069 style = Strings.defaultIfEmpty(unescapeAttributesSeparator(split[1]), null); 070 variation = Strings.defaultIfEmpty(unescapeAttributesSeparator(split[2]), null); 071 } 072 } 073 return new ResourceReference.UrlAttributes(locale, style, variation); 074 } 075 076 /** 077 * Reads resource reference attributes (style, locale, variation) encoded in the given URL. 078 * 079 * @param url 080 * the url containing the resource attributes 081 * @return the encoded attributes 082 * 083 * @see ResourceReference.UrlAttributes 084 */ 085 public static ResourceReference.UrlAttributes decodeResourceReferenceAttributes(Url url) 086 { 087 Args.notNull(url, "url"); 088 089 if (url.getQueryParameters().size() > 0) 090 { 091 Url.QueryParameter param = url.getQueryParameters().get(0); 092 if (Strings.isEmpty(param.getValue())) 093 { 094 return decodeResourceReferenceAttributes(param.getName()); 095 } 096 } 097 return new ResourceReference.UrlAttributes(null, null, null); 098 } 099 100 /** 101 * Encodes the given resource reference attributes returning the corresponding textual representation. 102 * 103 * @param attributes 104 * the resource reference attributes to encode 105 * @return the textual representation for the given attributes 106 * 107 * @see ResourceReference.UrlAttributes 108 */ 109 public static String encodeResourceReferenceAttributes(ResourceReference.UrlAttributes attributes) 110 { 111 if (attributes == null || 112 (attributes.getLocale() == null && attributes.getStyle() == null && attributes.getVariation() == null)) 113 { 114 return null; 115 } 116 else 117 { 118 StringBuilder res = new StringBuilder(32); 119 if (attributes.getLocale() != null) 120 { 121 res.append(attributes.getLocale()); 122 } 123 boolean styleEmpty = Strings.isEmpty(attributes.getStyle()); 124 if (!styleEmpty) 125 { 126 res.append('-'); 127 res.append(escapeAttributesSeparator(attributes.getStyle())); 128 } 129 if (!Strings.isEmpty(attributes.getVariation())) 130 { 131 if (styleEmpty) 132 { 133 res.append("--"); 134 } 135 else 136 { 137 res.append('-'); 138 } 139 res.append(escapeAttributesSeparator(attributes.getVariation())); 140 } 141 return res.toString(); 142 } 143 } 144 145 /** 146 * Encodes the attributes of the given resource reference in the specified url. 147 * 148 * @param url 149 * the resource reference attributes to encode 150 * @param reference 151 * 152 * @see ResourceReference.UrlAttributes 153 * @see Url 154 */ 155 public static void encodeResourceReferenceAttributes(Url url, ResourceReference reference) 156 { 157 Args.notNull(url, "url"); 158 Args.notNull(reference, "reference"); 159 160 String encoded = encodeResourceReferenceAttributes(reference.getUrlAttributes()); 161 if (!Strings.isEmpty(encoded)) 162 { 163 url.getQueryParameters().add(new Url.QueryParameter(encoded, "")); 164 } 165 } 166 167 /** 168 * Escapes any occurrences of <em>-</em> character in the style and variation 169 * attributes with <em>~</em>. Any occurrence of <em>~</em> is encoded as <em>~~</em>. 170 * 171 * @param attribute 172 * the attribute to escape 173 * @return the attribute with escaped separator character 174 */ 175 public static CharSequence escapeAttributesSeparator(String attribute) 176 { 177 CharSequence tmp = Strings.replaceAll(attribute, "~", "~~"); 178 return Strings.replaceAll(tmp, "-", "~"); 179 } 180 181 /** 182 * Parses the string representation of a {@link java.util.Locale} (for example 'en_GB'). 183 * 184 * @param locale 185 * the string representation of a {@link java.util.Locale} 186 * @return the corresponding {@link java.util.Locale} instance 187 */ 188 public static Locale parseLocale(String locale) 189 { 190 if (Strings.isEmpty(locale)) 191 { 192 return null; 193 } 194 else 195 { 196 String parts[] = locale.toLowerCase(Locale.ROOT).split("_", 3); 197 if (parts.length == 1) 198 { 199 return new Locale(parts[0]); 200 } 201 else if (parts.length == 2) 202 { 203 return new Locale(parts[0], parts[1]); 204 } 205 else if (parts.length == 3) 206 { 207 return new Locale(parts[0], parts[1], parts[2]); 208 } 209 else 210 { 211 return null; 212 } 213 } 214 } 215 216 /** 217 * read string with platform default encoding from resource stream 218 * 219 * @param resourceStream 220 * @return string read from resource stream 221 * 222 * @see #readString(org.apache.wicket.util.resource.IResourceStream, java.nio.charset.Charset) 223 */ 224 public static String readString(IResourceStream resourceStream) 225 { 226 return readString(resourceStream, null); 227 } 228 229 /** 230 * read string with specified encoding from resource stream 231 * 232 * @param resourceStream 233 * string source 234 * @param charset 235 * charset for the string encoding (use <code>null</code> for platform default) 236 * @return string read from resource stream 237 */ 238 public static String readString(IResourceStream resourceStream, Charset charset) 239 { 240 try 241 { 242 InputStream stream = resourceStream.getInputStream(); 243 244 try 245 { 246 byte[] bytes = IOUtils.toByteArray(stream); 247 248 if (charset == null) 249 { 250 charset = Charset.defaultCharset(); 251 } 252 253 return new String(bytes, charset.name()); 254 } 255 finally 256 { 257 resourceStream.close(); 258 } 259 } 260 catch (IOException e) 261 { 262 throw new WicketRuntimeException("failed to read string from " + resourceStream, e); 263 } 264 catch (ResourceStreamNotFoundException e) 265 { 266 throw new WicketRuntimeException("failed to locate stream from " + resourceStream, e); 267 } 268 } 269 270 /** 271 * Reverts the escaping applied by {@linkplain #escapeAttributesSeparator(String)} - unescapes 272 * occurrences of <em>~</em> character in the style and variation attributes with <em>-</em>. 273 * 274 * @param attribute 275 * the attribute to unescape 276 * @return the attribute with escaped separator character 277 */ 278 public static String unescapeAttributesSeparator(String attribute) 279 { 280 String tmp = ESCAPED_ATTRIBUTE_PATTERN.matcher(attribute).replaceAll("$1-$2"); 281 return Strings.replaceAll(tmp, "~~", "~").toString(); 282 } 283 284 private ResourceUtil() 285 { 286 // no-op 287 } 288}