KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > transformation > EncodeURLTransformer


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.transformation;
17
18 import java.io.IOException JavaDoc;
19 import java.util.Map JavaDoc;
20 import org.apache.avalon.framework.configuration.Configurable;
21 import org.apache.avalon.framework.configuration.Configuration;
22 import org.apache.avalon.framework.configuration.ConfigurationException;
23 import org.apache.avalon.framework.parameters.Parameters;
24 import org.apache.cocoon.ProcessingException;
25 import org.apache.cocoon.caching.CacheableProcessingComponent;
26 import org.apache.cocoon.environment.ObjectModelHelper;
27 import org.apache.cocoon.environment.Request;
28 import org.apache.cocoon.environment.Response;
29 import org.apache.cocoon.environment.Session;
30 import org.apache.cocoon.environment.SourceResolver;
31 import org.apache.cocoon.transformation.AbstractTransformer;
32 import org.apache.excalibur.source.SourceValidity;
33 import org.apache.excalibur.source.impl.validity.NOPValidity;
34 import org.apache.regexp.RE;
35 import org.apache.regexp.RESyntaxException;
36 import org.xml.sax.Attributes JavaDoc;
37 import org.xml.sax.SAXException JavaDoc;
38 import org.xml.sax.helpers.AttributesImpl JavaDoc;
39
40 /**
41  * @cocoon.sitemap.component.documentation
42  * The encodeURL transformer emits encoded URLs.
43  *
44  * @cocoon.sitemap.component.name encodeurl
45  * @cocoon.sitemap.component.logger sitemap.transformer.encodeURL
46  * @cocoon.sitemap.component.documentation.caching
47  * TBD
48  *
49  * @cocoon.sitemap.component.pooling.max 32
50  *
51  * The encodeURL transformer emits encoded URLs.
52  * <p>
53  * This transformer applies encodeURL method to URLs.
54  * You may want to use this transform to avoid doing the manually
55  * encodeURL() calls.
56  * </p>
57  * <p>
58  * Usually this transformer is appended as last transformer before
59  * the serialization process. In this case it is possible to encode
60  * URLs introduced in the generator, and xslt transformer phase.
61  * </p>
62  * <p>
63  * You can specify which attributes hold URL values in order to restrict
64  * URL rewriting to specific attributes only.
65  * </p>
66  * <p>
67  * Usage in a sitemap:
68  * </p>
69  * <pre><tt>
70  * &lt;map:composition&gt;
71  * ...
72  * &lt;map:transformers&gt;
73  * ...
74  * &lt;map:transformer type=&quot;encodeURL&quot;
75  * SRC=&quot;org.apache.cocoon.optional.transformation.EncodeURLTransformer&quot;&gt;
76  * &lt;exclude-name&gt;img/@src|a/@href=.&amp;asterik;adserver&lt;/exclude-name&gt;
77  * &lt;include-name&gt;.&amp;asterik;/@href|.&amp;asterik;/@src|.&amp;asterik;/@action&lt;/include-name&gt;
78  * &lt;/map:transformer&gt;
79  * ...
80  * &lt;map:pipelines&gt;
81  * &lt;map:pipeline&gt;
82  * ...
83  * &lt;map:transform type=&quot;encodeURL&quot;/&gt;
84  * ...
85  * </pre></tt>
86  *
87  * @author <a HREF="mailto:bh22351@i-one.at">Bernhard Huber</a>
88  * @version CVS $Id: EncodeURLTransformer.java 153376 2005-02-11 08:50:21Z cziegeler $
89  */

90 public class EncodeURLTransformer
91   extends AbstractTransformer
92   implements Configurable, CacheableProcessingComponent {
93
94     /**
95      * Configuration name for specifying excluding patterns,
96      * ie exclude-name.
97      */

98     public final static String JavaDoc EXCLUDE_NAME = "exclude-name";
99
100     /**
101      * Configuration name for specifying including patterns,
102      * ie include-name.
103      */

104     public final static String JavaDoc INCLUDE_NAME = "include-name";
105
106     /**
107      * Configuration default exclude pattern,
108      * ie img/@src
109      */

110     public final static String JavaDoc EXCLUDE_NAME_DEFAULT = "img/@src";
111
112     /**
113      * Configuration default exclude pattern,
114      * ie .*\/@href|.*\/@action|frame/@src
115      */

116     public final static String JavaDoc INCLUDE_NAME_DEFAULT = ".*/@href|.*/@action|frame/@src";
117
118     private String JavaDoc includeNameConfigure = INCLUDE_NAME_DEFAULT;
119     private String JavaDoc excludeNameConfigure = EXCLUDE_NAME_DEFAULT;
120
121     private ElementAttributeMatching elementAttributeMatching;
122     private Response response;
123     private boolean isEncodeURLNeeded;
124     private Session session;
125
126     /**
127      * check if encoding of URLs is neccessary.
128      *
129      * This is true if session object exists, and session-id
130      * was provided from URL, or session is new.
131      * The result is stored in some instance variables
132      */

133     protected void checkForEncoding(Request request) {
134         this.session = request.getSession(false);
135         this.isEncodeURLNeeded = false;
136         
137         if ( null != this.session ) {
138             // do encoding if session id is from URL, or the session is new,
139
// fixes BUG #13855, due to paint007@mc.duke.edu
140
if ( request.isRequestedSessionIdFromURL() || this.session.isNew()) {
141                 this.isEncodeURLNeeded = true;
142             }
143         }
144     }
145
146     /**
147      * Setup the transformer.
148      * <p>
149      * Setup include, and exclude patterns from the parameters
150      * </p>
151      *
152      * @param resolver source resolver
153      * @param objectModel sitemap objects
154      * @param parameters request parameters
155      *
156      */

157     public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc source, Parameters parameters)
158     throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
159
160         this.checkForEncoding(ObjectModelHelper.getRequest(objectModel));
161         
162         if (this.isEncodeURLNeeded) {
163             this.response = ObjectModelHelper.getResponse(objectModel);
164
165             // don't check if URL encoding is needed now, as
166
// a generator might create a new session
167
final String JavaDoc includeName = parameters.getParameter(INCLUDE_NAME,
168                                                                this.includeNameConfigure);
169             final String JavaDoc excludeName = parameters.getParameter(EXCLUDE_NAME,
170                                                                this.excludeNameConfigure);
171             try {
172                 this.elementAttributeMatching = new ElementAttributeMatching(includeName, excludeName);
173             } catch (RESyntaxException reex) {
174                 final String JavaDoc message = "Cannot parse include-name: " + includeName + " " +
175                     "or exclude-name: " + excludeName + "!";
176                 throw new ProcessingException(message, reex);
177             }
178         }
179     }
180
181
182     /**
183      * BEGIN SitemapComponent methods
184      *
185      * @param configuration Description of Parameter
186      * @exception ConfigurationException Description of Exception
187      */

188     public void configure(Configuration configuration) throws ConfigurationException {
189         Configuration child;
190
191         child = configuration.getChild(INCLUDE_NAME);
192         this.includeNameConfigure = child.getValue(INCLUDE_NAME_DEFAULT);
193
194         child = configuration.getChild(EXCLUDE_NAME);
195         this.excludeNameConfigure = child.getValue(EXCLUDE_NAME_DEFAULT);
196
197         if (this.includeNameConfigure == null) {
198             String JavaDoc message = "Configure " + INCLUDE_NAME + "!";
199             throw new ConfigurationException(message);
200         }
201         if (this.excludeNameConfigure == null) {
202             String JavaDoc message = "Configure " + EXCLUDE_NAME + "!";
203             throw new ConfigurationException(message);
204         }
205     }
206
207
208     /**
209      * Recycle resources of this transformer
210      */

211     public void recycle() {
212         super.recycle();
213         this.response = null;
214         this.session = null;
215         this.elementAttributeMatching = null;
216     }
217
218
219     /**
220      * Generate the unique key.
221      * This key must be unique inside the space of this component.
222      *
223      * @return The generated key hashes the src
224      */

225     public java.io.Serializable JavaDoc getKey() {
226         if (this.isEncodeURLNeeded) {
227             return null;
228         } else {
229             return "1";
230         }
231     }
232
233     /**
234      * Generate the validity object.
235      *
236      * @return The generated validity object or <code>null</code> if the
237      * component is currently not cacheable.
238      */

239     public SourceValidity getValidity() {
240         if (this.isEncodeURLNeeded) {
241             return null;
242         } else {
243             return NOPValidity.SHARED_INSTANCE;
244         }
245     }
246
247     /**
248      * Start parsing an element
249      *
250      * @param uri of the element
251      * @param name of the element
252      * @param raw name of the element
253      * @param attributes list
254      * @exception SAXException Description of Exception
255      */

256     public void startElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attributes)
257     throws SAXException JavaDoc {
258         if (this.isEncodeURLNeeded && this.elementAttributeMatching != null) {
259             String JavaDoc lname = name;
260             if (attributes != null && attributes.getLength() > 0) {
261                 AttributesImpl JavaDoc new_attributes = new AttributesImpl JavaDoc(attributes);
262                 for (int i = 0; i < new_attributes.getLength(); i++) {
263                     String JavaDoc attr_lname = new_attributes.getLocalName(i);
264
265                     String JavaDoc value = new_attributes.getValue(i);
266
267                     if (elementAttributeMatching.matchesElementAttribute(lname, attr_lname, value)) {
268                         // don't use simply this.response.encodeURL(value)
269
// but be more smart about the url encoding
270
final String JavaDoc new_value = this.encodeURL(value);
271                         if (getLogger().isDebugEnabled()) {
272                             this.getLogger().debug("element/@attribute matches: " + name + "/@" + attr_lname);
273                             this.getLogger().debug("encodeURL: " + value + " -> " + new_value);
274                         }
275                         new_attributes.setValue(i, new_value);
276                     }
277                 }
278                 // parent handles element using encoded attribute values
279
super.contentHandler.startElement(uri, name, raw, new_attributes);
280                 return;
281             }
282         }
283         // no match, parent handles element as-is
284
super.contentHandler.startElement(uri, name, raw, attributes);
285     }
286
287     /**
288      * Do the URL rewriting.
289      * <p>
290      * Check if <code>url</code> contains already the sessionid, some servlet-engines
291      * just appends the session-id without checking if the sessionid is already present.
292      * </p>
293      *
294      * @param url the URL probably without sessionid.
295      * @return String the original url inclusive the sessionid
296      */

297     private String JavaDoc encodeURL(String JavaDoc url) {
298         String JavaDoc encoded_url;
299         if (this.response != null) {
300             // As some servlet-engine does not check if url has been already rewritten
301
if (this.session != null && url.indexOf(this.session.getId()) > -1) {
302                 // url contains already the session id encoded
303
encoded_url = url;
304             } else {
305                 // do encode the session id
306
encoded_url = this.response.encodeURL(url);
307             }
308         } else {
309             encoded_url = url;
310         }
311         return encoded_url;
312     }
313     
314     /**
315      * A helper class for matching element names, and attribute names.
316      *
317      * <p>
318      * For given include-name, exclude-name decide if element-attribute pair
319      * matches. This class defines the precedence and matching algorithm.
320      * </p>
321      *
322      * @author <a HREF="mailto:bh22351@i-one.at">Bernhard Huber</a>
323      * @version CVS $Id: EncodeURLTransformer.java 153376 2005-02-11 08:50:21Z cziegeler $
324      */

325     public static class ElementAttributeMatching {
326         /**
327          * Regular expression of including patterns
328          *
329          */

330         protected RE includeNameRE;
331         /**
332          * Regular expression of excluding patterns
333          *
334          */

335         protected RE excludeNameRE;
336
337
338         /**
339          *Constructor for the ElementAttributeMatching object
340          *
341          * @param includeName Description of Parameter
342          * @param excludeName Description of Parameter
343          * @exception RESyntaxException Description of Exception
344          */

345         public ElementAttributeMatching(String JavaDoc includeName, String JavaDoc excludeName) throws RESyntaxException {
346             includeNameRE = new RE(includeName, RE.MATCH_CASEINDEPENDENT);
347             excludeNameRE = new RE(excludeName, RE.MATCH_CASEINDEPENDENT);
348         }
349
350
351         /**
352          * Return true iff element_name attr_name pair is not matched by exclude-name,
353          * but is matched by include-name
354          * @param element_name
355          * @param attr_name
356          * @param value TODO
357          *
358          * @return boolean true iff value of attribute_name should get rewritten, else
359          * false.
360          */

361         public boolean matchesElementAttribute(String JavaDoc element_name, String JavaDoc attr_name, String JavaDoc value) {
362             String JavaDoc element_attr_name = canonicalizeElementAttribute(element_name, attr_name, value);
363
364             if (excludeNameRE != null && includeNameRE != null) {
365                 return !matchesExcludesElementAttribute(element_attr_name) &&
366                         matchesIncludesElementAttribute(element_attr_name);
367             } else {
368                 return false;
369             }
370         }
371
372
373         /**
374          * Build from elementname, and attribute name a single string.
375          * <p>
376          * String concatenated <code>element name + "/@" + attribute name</code>
377          * is matched against the include and excluding patterns.
378          * </p>
379          * @param element_name Description of Parameter
380          * @param attr_name Description of Parameter
381          * @param value The value
382          *
383          * @return Description of the Returned Value
384          */

385         private String JavaDoc canonicalizeElementAttribute(String JavaDoc element_name, String JavaDoc attr_name, String JavaDoc value) {
386             return element_name + "/@" + attr_name + "=" + value;
387         }
388
389
390         /**
391          * Return true iff element_name attr_name pair is matched by exclude-name.
392          *
393          * @param element_attr_name
394          * @return boolean true iff exclude-name matches element_name, attr_name, else
395          * false.
396          */

397         private boolean matchesExcludesElementAttribute(String JavaDoc element_attr_name) {
398             boolean match = excludeNameRE.match(element_attr_name);
399             return match;
400         }
401
402
403         /**
404          * Return true iff element_name attr_name pair is matched by include-name.
405          *
406          * @param element_attr_name
407          * @return boolean true iff include-name matches element_name, attr_name, else
408          * false.
409          */

410         private boolean matchesIncludesElementAttribute(String JavaDoc element_attr_name) {
411             boolean match = includeNameRE.match(element_attr_name);
412             return match;
413         }
414     }
415 }
416
417
Popular Tags