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.request.mapper; 018 019import java.util.List; 020import java.util.Locale; 021 022import org.apache.wicket.request.IRequestMapper; 023import org.apache.wicket.request.Request; 024import org.apache.wicket.request.Url; 025import org.apache.wicket.request.Url.QueryParameter; 026import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder; 027import org.apache.wicket.request.mapper.parameter.PageParameters; 028import org.apache.wicket.util.lang.Args; 029import org.apache.wicket.util.string.Strings; 030 031/** 032 * 033 */ 034public abstract class AbstractMapper implements IRequestMapper 035{ 036 037 /** 038 * If the string is in a placeholder format ${key} this method returns the key. 039 * 040 * @param s 041 * @return placeholder key or <code>null</code> if string is not in right format 042 */ 043 protected String getPlaceholder(final String s) 044 { 045 return getPlaceholder(s, '$'); 046 } 047 048 /** 049 * If the string is in an optional parameter placeholder format #{key} this method returns the 050 * key. 051 * 052 * @param s 053 * @return placeholder key or <code>null</code> if string is not in right format 054 */ 055 protected String getOptionalPlaceholder(final String s) 056 { 057 return getPlaceholder(s, '#'); 058 } 059 060 /** 061 * If the string is in a placeholder format x{key}, where 'x' can be specified, this method 062 * returns the key. 063 * 064 * @param s 065 * @param startChar 066 * the character used to indicate the start of the placeholder 067 * @return placeholder key or <code>null</code> if string is not in right format 068 */ 069 protected String getPlaceholder(final String s, char startChar) 070 { 071 if (s == null || s.length() < 4) 072 { 073 return null; 074 } 075 else if (s.charAt(0) != startChar || s.charAt(1) != '{' || s.charAt(s.length() - 1) != '}') 076 { 077 return null; 078 } 079 else 080 { 081 return s.substring(2, s.length() - 1); 082 } 083 } 084 085 /** 086 * Construct. 087 */ 088 public AbstractMapper() 089 { 090 super(); 091 } 092 093 /** 094 * Returns true if the given url starts with specified segments. 095 * 096 * @param url 097 * @param segments 098 * @return <code>true</code> if the URL starts with the specified segments, <code>false</code> 099 * otherwise 100 */ 101 protected boolean urlStartsWith(final Url url, final String... segments) 102 { 103 if (url == null) 104 { 105 return false; 106 } 107 108 List<String> urlSegments = url.getSegments(); 109 110 for (int i = 0; i < segments.length; ++i) 111 { 112 String segment = segments[i]; 113 String urlSegment = safeSegmentGetter(urlSegments, i, null); 114 if (urlSegment == null && getOptionalPlaceholder(segment) == null) 115 { 116 // if the 'segment' has static value or is mandatory placeholder 117 return false; 118 } 119 else if (!segment.equals(urlSegment) && 120 (getPlaceholder(segment) == null && 121 getOptionalPlaceholder(segment) == null)) 122 { 123 return false; 124 } 125 } 126 127 return true; 128 } 129 130 /** 131 * Utility method to safely get an element from a list of String. 132 * If the specified index is bigger than the size of the list 133 * the default value is returned. 134 * 135 * @param segments 136 * @param index 137 * @param defaultValue 138 * @return the element at the specified position or the default value if the list size is smaller. 139 * 140 */ 141 protected String safeSegmentGetter(List<String> segments, int index, String defaultValue) 142 { 143 if (index < segments.size()) 144 { 145 return segments.get(index); 146 } 147 148 return defaultValue; 149 } 150 151 /** 152 * Extracts {@link PageParameters} from the URL using the given {@link IPageParametersEncoder} . 153 * 154 * @param request 155 * @param segmentsToSkip 156 * how many URL segments should be skipped because they "belong" to the 157 * {@link IRequestMapper} 158 * @param encoder 159 * @return PageParameters instance 160 */ 161 protected PageParameters extractPageParameters(final Request request, int segmentsToSkip, 162 final IPageParametersEncoder encoder) 163 { 164 Args.notNull(request, "request"); 165 Args.notNull(encoder, "encoder"); 166 167 // strip the segments and first query parameter from URL 168 Url urlCopy = new Url(request.getUrl()); 169 while ((segmentsToSkip > 0) && (urlCopy.getSegments().isEmpty() == false)) 170 { 171 urlCopy.getSegments().remove(0); 172 --segmentsToSkip; 173 } 174 175 if (!urlCopy.getQueryParameters().isEmpty() && 176 Strings.isEmpty(urlCopy.getQueryParameters().get(0).getValue())) 177 { 178 removeMetaParameter(urlCopy); 179 } 180 181 return encoder.decodePageParameters(urlCopy); 182 } 183 184 /** 185 * The new {@link IRequestMapper}s use the first query parameter to hold meta information about 186 * the request like page version, component version, locale, ... The actual 187 * {@link IRequestMapper} implementation can decide whether the this parameter should be removed 188 * before creating {@link PageParameters} from the current {@link Url#getQueryParameters() query 189 * parameters} 190 * 191 * @param urlCopy 192 * the {@link Url} that first query parameter has no value 193 */ 194 protected void removeMetaParameter(final Url urlCopy) 195 { 196 } 197 198 /** 199 * Encodes the given {@link PageParameters} to the URL using the given 200 * {@link IPageParametersEncoder}. The original URL object is unchanged. 201 * 202 * @param url 203 * @param pageParameters 204 * @param encoder 205 * @return URL with encoded parameters 206 */ 207 protected Url encodePageParameters(Url url, PageParameters pageParameters, 208 final IPageParametersEncoder encoder) 209 { 210 Args.notNull(url, "url"); 211 Args.notNull(encoder, "encoder"); 212 213 if (pageParameters == null) 214 { 215 pageParameters = new PageParameters(); 216 } 217 218 Url parametersUrl = encoder.encodePageParameters(pageParameters); 219 if (parametersUrl != null) 220 { 221 // copy the url 222 url = new Url(url); 223 224 for (String s : parametersUrl.getSegments()) 225 { 226 url.getSegments().add(s); 227 } 228 for (QueryParameter p : parametersUrl.getQueryParameters()) 229 { 230 url.getQueryParameters().add(p); 231 } 232 } 233 234 return url; 235 } 236 237 /** 238 * Convenience method for representing mountPath as array of segments 239 * 240 * @param mountPath 241 * @return array of path segments 242 */ 243 protected String[] getMountSegments(String mountPath) 244 { 245 if (mountPath.charAt(0) == '/') 246 { 247 mountPath = mountPath.substring(1); 248 } 249 Url url = Url.parse(mountPath); 250 251 String[] res = new String[url.getSegments().size()]; 252 for (int i = 0; i < res.length; ++i) 253 { 254 res[i] = url.getSegments().get(i); 255 } 256 return res; 257 } 258 259 /** 260 * @return the locale to use for parsing any numbers in the request parameters 261 */ 262 protected Locale resolveLocale() 263 { 264 return Locale.getDefault(Locale.Category.DISPLAY); 265 } 266}