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: */
017: package org.apache.wicket.request.target.coding;
018:
019: import java.io.UnsupportedEncodingException;
020: import java.net.URLDecoder;
021: import java.net.URLEncoder;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.TreeMap;
025: import java.util.Map.Entry;
026:
027: import org.apache.wicket.Application;
028: import org.apache.wicket.protocol.http.UnitTestSettings;
029: import org.apache.wicket.util.string.AppendingStringBuffer;
030: import org.apache.wicket.util.string.Strings;
031: import org.apache.wicket.util.value.ValueMap;
032: import org.slf4j.Logger;
033: import org.slf4j.LoggerFactory;
034:
035: /**
036: * Abstract class for mount encoders that uses paths and forward slashes.
037: *
038: * @author Eelco Hillenius
039: * @author Igor Vaynberg (ivaynberg)
040: */
041: public abstract class AbstractRequestTargetUrlCodingStrategy implements
042: IRequestTargetUrlCodingStrategy,
043: IMountableRequestTargetUrlCodingStrategy {
044: /** log. */
045: private static final Logger log = LoggerFactory
046: .getLogger(AbstractRequestTargetUrlCodingStrategy.class);
047:
048: /** mounted path. */
049: private final String mountPath;
050:
051: /**
052: * Construct.
053: *
054: * @param mountPath
055: * the mount path
056: */
057: public AbstractRequestTargetUrlCodingStrategy(final String mountPath) {
058: if (mountPath == null) {
059: throw new IllegalArgumentException(
060: "Mount path cannot be null or empty");
061: }
062: this .mountPath = mountPath.startsWith("/") ? mountPath
063: .substring(1) : mountPath;
064: if (this .mountPath.startsWith("resources/")
065: || this .mountPath.equals("resources")) {
066: throw new IllegalArgumentException(
067: "Mount path cannot be under '/resources'");
068: }
069: }
070:
071: /**
072: * @see org.apache.wicket.request.target.coding.IMountableRequestTargetUrlCodingStrategy#getMountPath()
073: */
074: public final String getMountPath() {
075: return mountPath;
076: }
077:
078: /**
079: * Encodes Map into a url fragment and append that to the provided url
080: * buffer.
081: *
082: * @param url
083: * url so far
084: *
085: * @param parameters
086: * Map object to be encoded
087: */
088: protected void appendParameters(AppendingStringBuffer url,
089: Map parameters) {
090: if (parameters != null && parameters.size() > 0) {
091: final Iterator entries;
092: if (UnitTestSettings.getSortUrlParameters()) {
093: entries = new TreeMap(parameters).entrySet().iterator();
094: } else {
095: entries = parameters.entrySet().iterator();
096: }
097: while (entries.hasNext()) {
098: Map.Entry entry = (Entry) entries.next();
099: Object value = entry.getValue();
100: if (value != null) {
101: if (value instanceof String[]) {
102: String[] values = (String[]) value;
103: for (int i = 0; i < values.length; i++) {
104: appendValue(url, entry.getKey().toString(),
105: values[i]);
106: }
107: } else {
108: appendValue(url, entry.getKey().toString(),
109: value.toString());
110: }
111: }
112: }
113: }
114: }
115:
116: private void appendValue(AppendingStringBuffer url, String key,
117: String value) {
118: String escapedValue = urlEncode(value);
119: if (!Strings.isEmpty(escapedValue)) {
120: if (!url.endsWith("/")) {
121: url.append("/");
122: }
123: url.append(key).append("/").append(escapedValue)
124: .append("/");
125: }
126: }
127:
128: /**
129: * Decodes parameters object from the provided url fragment
130: *
131: * @param urlFragment
132: * fragment of the url after the decoded path and before the
133: * query string
134: * @param urlParameters
135: * query string parameters
136: * @return Parameters created from the url fragment and query string
137: */
138: protected ValueMap decodeParameters(String urlFragment,
139: Map urlParameters) {
140: // Hack off any leading slash
141: if (urlFragment.startsWith("/")) {
142: urlFragment = urlFragment.substring(1);
143: }
144: // Hack off any trailing slash
145: if (urlFragment.length() > 0 && urlFragment.endsWith("/")) {
146: urlFragment = urlFragment.substring(0,
147: urlFragment.length() - 1);
148: }
149:
150: if (urlFragment.length() == 0) {
151: return new ValueMap();
152: }
153:
154: // Split into pairs
155: final String[] pairs = urlFragment.split("/");
156:
157: // If we don't have an even number of pairs
158: if (pairs.length % 2 != 0) {
159: // give up
160: throw new IllegalStateException(
161: "URL fragment has unmatched key/value " + "pair: "
162: + urlFragment);
163: }
164:
165: // Loop through pairs
166:
167: ValueMap parameters = new ValueMap();
168: for (int i = 0; i < pairs.length; i += 2) {
169: String value = pairs[i + 1];
170: value = urlDecode(value);
171: parameters.add(pairs[i], value);
172: }
173:
174: if (urlParameters != null) {
175: parameters.putAll(urlParameters);
176: }
177:
178: return parameters;
179: }
180:
181: /**
182: * Returns a decoded value of the given value
183: *
184: * @param value
185: * @return Decodes the value
186: */
187: protected String urlDecode(String value) {
188: try {
189: value = URLDecoder.decode(value, Application.get()
190: .getRequestCycleSettings()
191: .getResponseRequestEncoding());
192: } catch (UnsupportedEncodingException ex) {
193: log.error("error decoding parameter", ex);
194: }
195: return value;
196: }
197:
198: /**
199: * Url encodes a string
200: *
201: * @param string
202: * string to be encoded
203: * @return encoded string
204: */
205: protected String urlEncode(String string) {
206: try {
207: return URLEncoder.encode(string, Application.get()
208: .getRequestCycleSettings()
209: .getResponseRequestEncoding());
210: } catch (UnsupportedEncodingException e) {
211: log.error(e.getMessage(), e);
212: return string;
213: }
214:
215: }
216:
217: public boolean matches(String path) {
218: if (path.startsWith(mountPath)) {
219: /*
220: * We need to match /mount/point or
221: * /mount/point/with/extra/path, but not /mount/pointXXX
222: */
223: String remainder = path.substring(mountPath.length());
224: if (remainder.length() == 0 || remainder.startsWith("/")) {
225: return true;
226: }
227: }
228: return false;
229: }
230: }
|