KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Copyright 1999-2005 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.HashSet JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.Map JavaDoc;
22 import java.util.Set JavaDoc;
23 import java.util.StringTokenizer JavaDoc;
24
25 import org.apache.avalon.framework.activity.Disposable;
26 import org.apache.avalon.framework.activity.Initializable;
27 import org.apache.avalon.framework.configuration.Configuration;
28 import org.apache.avalon.framework.configuration.ConfigurationException;
29 import org.apache.avalon.framework.parameters.Parameters;
30 import org.apache.cocoon.ProcessingException;
31 import org.apache.cocoon.components.modules.input.InputModuleHelper;
32 import org.apache.cocoon.environment.SourceResolver;
33 import org.apache.cocoon.transformation.helpers.VariableConfiguration;
34 import org.xml.sax.Attributes JavaDoc;
35 import org.xml.sax.SAXException JavaDoc;
36 import org.xml.sax.helpers.AttributesImpl JavaDoc;
37
38 /**
39  * Rewrites URIs in links to a value determined by an InputModule.
40  * The URI scheme identifies the InputModule to use, and the rest of the URI is
41  * used as the attribute name.
42  * <h3>Example</h3>
43  * For instance, if we had an {@link
44  * org.apache.cocoon.components.modules.input.XMLFileModule}, configured to
45  * read values from an XML file:
46  * <pre>
47  * &lt;site>
48  * &lt;faq>
49  * &lt;how_to_boil_eggs HREF="faq/eggs.html"/>
50  * &lt;/faq>
51  * &lt;/site>
52  * </pre>
53  * mapped to the prefix 'site:', then &lt;link
54  * HREF="site:/site/faq/how_to_boil_eggs/@href"> would be replaced with
55  * &lt;link HREF="faq/eggs.html"&gt;
56  * <p>
57  * InputModules are configured twice; first statically in
58  * <code>cocoon.xconf</code>, and then dynamically at runtime, with dynamic
59  * configuration (if any) taking precedence. VariableRewriterTransformer allows
60  * you to pass a dynamic configuration to used InputModules as follows.
61  * <p>
62  * First, a template Configuration is specified in the static
63  * &lt;map:components> block of the sitemap:
64  * <pre>
65  * &lt;map:transformer name="linkrewriter"
66  * SRC="org.apache.cocoon.transformation.VariableRewriterTransformer">
67  * &lt;input-module name="site" SRC="cocoon://samples/link/linkmap" reloadable="true"/>
68  * &lt;input-module name="mapper">
69  * &lt;input-module name="site" SRC="{src}" reloadable="true"/>
70  * &lt;prefix>/site/&lt;/prefix>
71  * &lt;suffix>/@href&lt;/suffix>
72  * &lt;/input-module>
73  * &lt;/map:transformer>
74  * </pre>
75  * Here, we have established dynamic configuration templates for two modules,
76  * 'site' (an {@link org.apache.cocoon.components.modules.input.XMLFileModule}
77  * and 'mapper' (A {@link
78  * org.apache.cocoon.components.modules.input.SimpleMappingMetaModule}. All
79  * other InputModules will use their static configs. Note that the dynamic
80  * config syntax different to the static config syntax (attributes instead of
81  * elements). Note also that, when configuring a Meta InputModule like
82  * 'mapper', we need to also configure the 'inner' module (here, 'site') with a
83  * nested &lt;input-module>.
84  * <p>
85  * There is one further twist; to have <em>really</em> dynamic configuration,
86  * we need information available only when the transformer actually runs. This
87  * is why the above config was called a "template" Configuration; it needs to
88  * be 'instantiated' and provided extra info, namely:
89  * <ul>
90  * <li>The {src} string will be replaced with the map:transform @src attribute value.
91  * <li>Any other {variables} will be replaced with map:parameter values
92  * </ul>
93  * With the above config template, we can have a matcher like:
94  *
95  * <pre>
96  * &lt;map:match pattern="**welcome">
97  * &lt;map:generate SRC="index.xml"/>
98  * &lt;map:transform type="linkrewriter" SRC="cocoon:/{1}linkmap"/>
99  * &lt;map:serialize type="xml"/>
100  * &lt;/map:match>
101  * </pre>
102  *
103  * Which would cause the 'mapper' XMLFileModule to be configured with a
104  * different XML file, depending on the request.
105  * <p>
106  * Similarly, we could use a dynamic prefix:
107  * <pre>
108  * &lt;prefix>{prefix}&lt;/prefix>
109  * </pre>
110  * in the template config, and:
111  * <pre>
112  * &lt;map:parameter name="prefix" value="/site/"/>
113  * </pre>
114  * in the map:transform
115  * <p>
116  *
117  * <h3>Configuration</h3>
118  * <p>
119  * The following map:parameter's are recognised:
120  * <dl>
121  * <dt>link-attrs</dt>
122  * <dd>Space-separated list of attributes to consider links (to be
123  * transformed). Defaults to 'href'.</dd>
124  * <dt>schemes</dt>
125  * <dd>Space-separated list of URI schemes to explicitly include. If specified, all URIs with unlisted schemes will not be converted.</dd>
126  * <dt>exclude-schemes</dt>
127  * <dd>Space-separated list of URI schemes to explicitly exclude.</dd>
128  * <dt>bad-link-str</dt>
129  * <dd>String to use for links with a correct InputModule prefix, but no value
130  * therein. Defaults to the original URI.</dd>
131  * </dl>
132  *
133  * <p>
134  * Note that currently, only links in the default ("") namespace are converted.
135  *
136  * @author <a HREF="mailto:jefft@apache.org">Jeff Turner</a>
137  * @version $Id: VariableRewriterTransformer.java 168366 2005-05-05 18:23:18Z vgritsenko $
138  */

139 public class VariableRewriterTransformer extends AbstractSAXTransformer
140                                          implements Initializable, Disposable {
141
142     private static final String JavaDoc NAMESPACE = "";
143
144     /** A list of attributes considered 'links' */
145     private Set JavaDoc linkAttrs;
146
147     /** List containing schemes (protocols) of links to log */
148     private Set JavaDoc inSchemes;
149     private Set JavaDoc outSchemes;
150
151     /** Configuration passed to the component once through configure(). */
152     private Configuration origConf;
153
154     /** Derivation of origConf with variables obtained from setup() parameters.
155      * Recreated once per invocation. */

156     private Configuration conf;
157
158     private InputModuleHelper modHelper;
159
160     private String JavaDoc badLinkStr;
161
162     /**
163      * Configure this component from the map:transformer block. Called before
164      * initialization and setup.
165      */

166     public void configure(Configuration conf)
167     throws ConfigurationException {
168         super.configure(conf);
169         this.origConf = conf;
170     }
171
172     /**
173      * Initiate resources prior to this component becoming active.
174      */

175     public void initialize() throws Exception JavaDoc {
176         this.defaultNamespaceURI = NAMESPACE;
177         this.modHelper = new InputModuleHelper();
178         this.modHelper.setup(this.manager);
179     }
180
181     /**
182      * Setup this component to handle a map:transform instance.
183      */

184     public void setup(SourceResolver resolver, Map JavaDoc objectModel,
185             String JavaDoc src, Parameters parameters)
186         throws ProcessingException, SAXException JavaDoc, IOException JavaDoc
187     {
188         super.setup(resolver, objectModel, src, parameters);
189         this.badLinkStr = parameters.getParameter("bad-link-str", null);
190         this.linkAttrs = split(parameters.getParameter("link-attrs", "href"), " ");
191         this.inSchemes = split(parameters.getParameter("schemes", ""), " ");
192         this.outSchemes = split(parameters.getParameter("exclude-schemes", ""), " ");
193
194         // Generate conf
195
VariableConfiguration varConf = new VariableConfiguration(this.origConf);
196         varConf.addVariable("src", src);
197         varConf.addVariables(parameters);
198         try {
199             this.conf = varConf.getConfiguration();
200         } catch (ConfigurationException ce) {
201             throw new ProcessingException("Couldn't create dynamic config ", ce);
202         }
203     }
204
205     /** Split a string into a Set of strings.
206      * @param str String to split
207      * @param delim Delimiter character
208      * @return A Set of strings in 'str'
209      */

210     private Set JavaDoc split(String JavaDoc str, String JavaDoc delim) {
211         Set JavaDoc schemes = new HashSet JavaDoc();
212         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(str, delim);
213         while (st.hasMoreTokens()) {
214             String JavaDoc pfx = st.nextToken();
215             schemes.add(pfx);
216         }
217         return schemes;
218     }
219
220
221     /**
222      * Start processing elements of our namespace.
223      * This hook is invoked for each sax event with our namespace.
224      * @param uri The namespace of the element.
225      * @param name The local name of the element.
226      * @param raw The qualified name of the element.
227      * @param attr The attributes of the element.
228      */

229     public void startTransformingElement(String JavaDoc uri,
230             String JavaDoc name,
231             String JavaDoc raw,
232             Attributes JavaDoc attr)
233         throws ProcessingException, IOException JavaDoc, SAXException JavaDoc
234     {
235         Attributes JavaDoc newAttrs = null;
236         boolean matched = false;
237
238         Iterator JavaDoc iter = linkAttrs.iterator();
239         while (iter.hasNext()) {
240             int attrIdx = attr.getIndex((String JavaDoc)iter.next());
241             if (attrIdx != -1) {
242                 String JavaDoc oldAttr = attr.getValue(attrIdx);
243                 int i = oldAttr.indexOf(":");
244                 if (i != -1) {
245                     String JavaDoc scheme = oldAttr.substring(0, i);
246                     String JavaDoc addr = oldAttr.substring(i+1);
247                     if (outSchemes.contains(scheme)) {
248                         if (getLogger().isDebugEnabled()) {
249                             getLogger().debug("Ignoring link '"+scheme+":"+addr+"'");
250                         }
251                     } else if (inSchemes.contains(scheme)) {
252                         matched = true;
253                         newAttrs = getLinkAttr(attr, attrIdx, scheme, addr);
254                         if (getLogger().isDebugEnabled()) {
255                             getLogger().debug("Converted link '"+oldAttr+"' to '"+newAttrs.getValue(attrIdx)+"'");
256                         }
257                     } else {
258                         if (inSchemes.size() == 0) {
259                             // If the link wasn't deliberately excluded from a
260
// list of 'good' links, then include it.
261
matched = true;
262                             newAttrs = getLinkAttr(attr, attrIdx, scheme, addr);
263                             getLogger().debug("Converted link '"+oldAttr+"' to '"+newAttrs.getValue(attrIdx)+"'");
264                         }
265                     }
266                 }
267             }
268         }
269         if (matched) {
270             super.startTransformingElement(uri, name, raw, newAttrs);
271         } else {
272             super.startTransformingElement(uri, name, raw, attr);
273         }
274     }
275
276     /**
277      * Process the SAX event.
278      */

279     public void characters(char[] p0, int p1, int p2)
280     throws SAXException JavaDoc {
281         if (this.ignoreEventsCount == 0) {
282             if (this.ignoreEmptyCharacters == true) {
283                 String JavaDoc value = new String JavaDoc(p0, p1, p2);
284                 if (value.trim().length() > 0) {
285                     super.characters(p0, p1, p2);
286                 }
287             } else {
288                 super.characters(p0, p1, p2);
289             }
290         }
291     }
292
293     /**
294      * Rewrite link in a set of attributes.
295      *
296      * @param oldAttrs Attributes containing unconverted link.
297      * @param linkIndex index of link to convert
298      * @param scheme URI scheme (indicating InputModule) of link
299      * @param addr URI scheme of link
300      * @return an Attributes based on <code>oldAttrs</code>, but with one attribute rewritten.
301      */

302     private Attributes JavaDoc getLinkAttr(Attributes JavaDoc oldAttrs, int linkIndex, String JavaDoc scheme, String JavaDoc addr) {
303         AttributesImpl JavaDoc newAttrs = new AttributesImpl JavaDoc(oldAttrs);
304         try {
305             String JavaDoc modValue = (String JavaDoc)modHelper.getAttribute(this.objectModel, getConf(scheme), scheme, addr, (badLinkStr!=null?badLinkStr:scheme+":"+addr));
306             newAttrs.setValue(linkIndex, modValue);
307         } catch (Exception JavaDoc e) {
308             // Swallow IM errors, usually prefixes like 'http' that aren't
309
// bound to an InputModule.
310
getLogger().warn("## IM error: "+e, e);
311         }
312         return newAttrs;
313     }
314
315     /**
316      * Retrieve a dynamic Configuration for a specific InputModule.
317      * @param scheme InputModule name
318      * @return Configuration for specified scheme, from the map:transformer block.
319      */

320     private Configuration getConf(String JavaDoc scheme) {
321         Configuration[] schemeConfs = this.conf.getChildren();
322         for (int i=0; i<schemeConfs.length; i++) {
323             if (scheme.equals(schemeConfs[i].getAttribute("name", null))) {
324                 return schemeConfs[i];
325             }
326         }
327         return null;
328     }
329
330     /** Recycle this component for use in another map:transform. */
331     public void recycle() {
332         this.resolver = null;
333         this.linkAttrs = null;
334         this.inSchemes = null;
335         this.outSchemes = null;
336         this.conf = null;
337         // Note: configure() and initialize() are not called after every
338
//recycle, so don't null origConf
339
super.recycle();
340     }
341
342     /* (non-Javadoc)
343      * @see org.apache.avalon.framework.activity.Disposable#dispose()
344      */

345     public void dispose() {
346         if (this.modHelper != null) {
347             this.modHelper.releaseAll();
348             this.modHelper = null;
349         }
350         super.dispose();
351     }
352 }
353
Popular Tags