001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package org.wings;
014:
015: import org.wings.io.Device;
016: import org.wings.util.SStringBuilder;
017: import org.wings.session.Session;
018: import org.wings.session.SessionManager;
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021:
022: import java.io.IOException;
023: import java.io.UnsupportedEncodingException;
024: import java.net.URLEncoder;
025:
026: /**
027: * Handles a HTTP GET Address that can be updated with additional parameters.
028: *
029: * @author <a href="mailto:haaf@mercatis.de">Armin Haaf</a>
030: */
031: public class RequestURL extends SimpleURL {
032: private static final Log log = LogFactory.getLog(RequestURL.class);
033:
034: private static final String DEFAULT_RESOURCE_NAME = "_";
035:
036: private String baseParameters;
037:
038: private boolean hasQuestMark;
039:
040: private String eventEpoch;
041:
042: private String resource;
043:
044: private SStringBuilder parameters = null;
045:
046: /**
047: * Current session encoding used for URLEncoder.
048: */
049: private final String characterEncoding;
050:
051: public RequestURL() {
052: this .characterEncoding = determineCharacterEncoding();
053: }
054:
055: /**
056: * copy constructor.
057: */
058: private RequestURL(RequestURL other) {
059: this .characterEncoding = determineCharacterEncoding();
060: this .baseURL = other.baseURL;
061: this .baseParameters = other.baseParameters;
062: this .hasQuestMark = other.hasQuestMark;
063: this .eventEpoch = other.eventEpoch;
064: this .resource = other.resource;
065: SStringBuilder params = other.parameters;
066: parameters = (params == null) ? null : new SStringBuilder(
067: params.toString());
068: }
069:
070: public RequestURL(String baseURL, String encodedBaseURL) {
071: this .characterEncoding = determineCharacterEncoding();
072: setBaseURL(baseURL, encodedBaseURL);
073: }
074:
075: public void setEventEpoch(String e) {
076: eventEpoch = e;
077: }
078:
079: public String getEventEpoch() {
080: return eventEpoch;
081: }
082:
083: public void setResource(String r) {
084: resource = r;
085: }
086:
087: public String getResource() {
088: return resource;
089: }
090:
091: public void setBaseURL(String b, String encoded) {
092: baseURL = b;
093:
094: baseParameters = encoded.substring(b.length());
095: if (baseParameters.length() == 0)
096: baseParameters = null;
097:
098: if (baseParameters != null)
099: hasQuestMark = baseParameters.indexOf('?') >= 0;
100: else
101: hasQuestMark = false;
102: }
103:
104: /**
105: * Add an additional parameter to be included in the GET paramter
106: * list. Usually, this paramter will be in the form 'name=value'.
107: *
108: * @param parameter to be included in the GET parameters.
109: * @return a reference to <code>this</code> to simplify 'call chaining'
110: */
111: public RequestURL addParameter(String parameter) {
112: if (parameter != null) {
113: if (parameters == null)
114: parameters = new SStringBuilder();
115: else
116: parameters.append("&");
117: parameters.append(recode(parameter));
118: }
119: return this ;
120: }
121:
122: /**
123: * Add an additional name/value pair to be included in the GET paramter
124: * list. The added parameter will be 'name=value'
125: *
126: * @param name the name of the parameter
127: * @param value the value of the parameter
128: * @return a reference to <code>this</code> to simplify 'call chaining'
129: */
130: public RequestURL addParameter(String name, String value) {
131: addParameter(name);
132: parameters.append("=").append(recode(value));
133: return this ;
134: }
135:
136: /**
137: * Add an additional name/value pair to be included in the GET paramter
138: * list. The added name will be the LowLevelEventId of the LowLevelEventListener.
139: *
140: * @param value the value of the parameter
141: * @return a reference to <code>this</code> to simplify 'call chaining'
142: */
143: public RequestURL addParameter(LowLevelEventListener comp,
144: String value) {
145: addParameter(comp.getLowLevelEventId(), value);
146: return this ;
147: }
148:
149: /**
150: * Add an additional name/value pair to be included in the GET paramter
151: * list. The added parameter will be 'name=value'
152: *
153: * @param name the name of the parameter
154: * @param value the value of the parameter
155: * @return a reference to <code>this</code> to simplify 'call chaining'
156: */
157: public RequestURL addParameter(String name, int value) {
158: addParameter(name);
159: parameters.append("=").append(value);
160: return this ;
161: }
162:
163: /**
164: * clear all additional paramters given in the {@link #addParameter(String)} call.
165: */
166: public void clear() {
167: if (parameters != null) {
168: parameters.setLength(0);
169: }
170: setEventEpoch(null);
171: setResource(null);
172: }
173:
174: /**
175: * Writes the context Address to the output Device. Appends all
176: * parameters given. Only the context URL is given, since all GET urls generated
177: * by wings are relative to the WingS servlet.
178: * Tries to avoid charset conversion as much as possible by precalculating the
179: * byteArray representation of the non-parameter part.
180: *
181: * @param d the Device to write to
182: */
183: public void write(Device d) throws IOException {
184: super .write(d);
185:
186: if (resource != null && eventEpoch != null) {
187: d.print(eventEpoch);
188: d.print(SConstants.UID_DIVIDER);
189: }
190:
191: if (resource != null) {
192: d.print(resource);
193: } else {
194: /*
195: * The default resource name. Work around a bug in some
196: * browsers that fail to assemble URLs.
197: * (TODO: verify and give better explanation here).
198: */
199: d.print(DEFAULT_RESOURCE_NAME);
200: }
201:
202: if (baseParameters != null) {
203: d.print(baseParameters);
204: }
205:
206: if (parameters != null && parameters.length() > 0) {
207: d.print(hasQuestMark ? "&" : "?");
208: d.print(parameters.toString());
209: }
210: }
211:
212: /**
213: * Returns the string representation of the context URL plus
214: * all paramters given.
215: */
216: public String toString() {
217: SStringBuilder erg = new SStringBuilder();
218:
219: if (baseURL != null) {
220: erg.append(baseURL);
221: }
222:
223: if (resource != null && eventEpoch != null) {
224: erg.append(eventEpoch);
225: erg.append(SConstants.UID_DIVIDER);
226: }
227:
228: if (resource != null) {
229: erg.append(resource);
230: } else {
231: erg.append(DEFAULT_RESOURCE_NAME);
232: }
233:
234: if (baseParameters != null) {
235: erg.append(baseParameters);
236: }
237:
238: if (parameters != null && parameters.length() > 0) {
239: erg.append(hasQuestMark ? "&" : "?");
240: erg.append(parameters.toString());
241: }
242:
243: return erg.toString();
244: }
245:
246: public boolean equals(Object o) {
247: if (o == null)
248: return false;
249: if (!super .equals(o))
250: return false;
251: RequestURL other = (RequestURL) o;
252: return (hasQuestMark == other.hasQuestMark
253: && eq(baseParameters, other.baseParameters)
254: && eq(eventEpoch, other.eventEpoch)
255: && eq(resource, other.resource) && eq(parameters,
256: other.parameters));
257: }
258:
259: /**
260: * @see java.lang.Object#hashCode()
261: */
262: public int hashCode() {
263: return baseURL != null ? baseURL.hashCode() : 0;
264: }
265:
266: /**
267: * Deep copy.
268: *
269: * @return object with cloned contents
270: */
271: public Object clone() {
272: return new RequestURL(this );
273: }
274:
275: private final boolean eq(Object a, Object b) {
276: return (a == b) || (a != null && a.equals(b));
277: }
278:
279: /**
280: * Determine the current character encoding.
281: * @return Character encoding string or <code>null</code>
282: */
283: private String determineCharacterEncoding() {
284: String characterEncoding = null;
285: final Session session = SessionManager.getSession();
286: if (session != null) {
287: characterEncoding = session.getCharacterEncoding();
288: }
289: return characterEncoding;
290: }
291:
292: /**
293: * Recoded passes string to URL with current encoding.
294: * @param parameter String to recode
295: * @return Encoded parameter or same if an error occured
296: */
297: private String recode(String parameter) {
298: try {
299: return URLEncoder.encode(parameter, characterEncoding);
300: } catch (UnsupportedEncodingException e) {
301: log.warn("Unknown character encoding?! "
302: + characterEncoding, e);
303: return parameter;
304: }
305: }
306: }
|