KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > i18n > XMLResourceBundle


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.i18n;
17
18 import org.apache.avalon.framework.logger.AbstractLogEnabled;
19 import org.apache.excalibur.source.Source;
20 import org.apache.excalibur.source.SourceNotFoundException;
21 import org.apache.excalibur.source.SourceResolver;
22 import org.apache.excalibur.source.SourceValidity;
23 import org.apache.excalibur.source.impl.validity.ExpiresValidity;
24
25 import org.apache.cocoon.ResourceNotFoundException;
26 import org.apache.cocoon.components.source.SourceUtil;
27 import org.apache.cocoon.components.source.impl.validity.DelayedValidity;
28 import org.apache.cocoon.xml.ParamSaxBuffer;
29
30 import org.xml.sax.Attributes JavaDoc;
31 import org.xml.sax.ContentHandler JavaDoc;
32 import org.xml.sax.Locator JavaDoc;
33 import org.xml.sax.SAXException JavaDoc;
34
35 import java.net.MalformedURLException JavaDoc;
36 import java.util.Collections JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.Locale JavaDoc;
39 import java.util.Map JavaDoc;
40
41 /**
42  * Implementation of <code>Bundle</code> interface for XML resources. Represents a
43  * single XML message bundle.
44  *
45  * <p>
46  * XML format for this resource bundle implementation is the following:
47  * <pre>
48  * &lt;catalogue xml:lang="en"&gt;
49  * &lt;message key="key1"&gt;Message &lt;br/&gt; Value 1&lt;/message&gt;
50  * &lt;message key="key2"&gt;Message &lt;br/&gt; Value 1&lt;/message&gt;
51  * ...
52  * &lt;/catalogue&gt;
53  * </pre>
54  *
55  * <p>
56  * Value can be any well formed XML snippet and it will be cached by the key specified
57  * in the attribute <code>key</code>. Objects returned by this {@link Bundle} implementation
58  * are instances of the {@link ParamSaxBuffer} class.
59  *
60  * <p>
61  * If value for a key is not present in this bundle, parent bundle will be queried.
62  *
63  * @author <a HREF="mailto:dev@cocoon.apache.org">Apache Cocoon Team</a>
64  * @version $Id: XMLResourceBundle.java 330021 2005-11-01 10:37:24Z sylvain $
65  */

66 public class XMLResourceBundle extends AbstractLogEnabled
67                                implements Bundle {
68
69     /**
70      * Namespace for the Bundle markup
71      */

72     public static final String JavaDoc NS = "http://apache.org/cocoon/i18n/2.0";
73
74     /**
75      * XML bundle root element name
76      */

77     public static final String JavaDoc EL_CATALOGUE = "catalogue";
78
79     /**
80      * XML bundle message element name
81      */

82     public static final String JavaDoc EL_MESSAGE = "message";
83
84     /**
85      * XML bundle message element's key attribute name
86      */

87     public static final String JavaDoc AT_KEY = "key";
88
89     /**
90      * Source URI of the bundle
91      */

92     private String JavaDoc sourceURI;
93
94     /**
95      * Bundle validity
96      */

97     private SourceValidity validity;
98
99     /**
100      * Locale of the bundle
101      */

102     private Locale JavaDoc locale;
103
104     /**
105      * Parent of the current bundle
106      */

107     protected Bundle parent;
108
109     /**
110      * Objects stored in the bundle
111      */

112     protected Map JavaDoc values;
113
114
115     /**
116      * Processes XML bundle file and creates map of values
117      */

118     private static class SAXContentHandler implements ContentHandler JavaDoc {
119         private Map JavaDoc values;
120         private int state;
121         private String JavaDoc namespace;
122         private ParamSaxBuffer buffer;
123
124         public SAXContentHandler(Map JavaDoc values) {
125             this.values = values;
126         }
127
128         public void setDocumentLocator(Locator JavaDoc arg0) {
129             // Ignore
130
}
131
132         public void startDocument() throws SAXException JavaDoc {
133             // Ignore
134
}
135
136         public void endDocument() throws SAXException JavaDoc {
137             // Ignore
138
}
139
140         public void processingInstruction(String JavaDoc arg0, String JavaDoc arg1) throws SAXException JavaDoc {
141             // Ignore
142
}
143
144         public void skippedEntity(String JavaDoc arg0) throws SAXException JavaDoc {
145             // Ignore
146
}
147
148         public void startElement(String JavaDoc ns, String JavaDoc localName, String JavaDoc qName, Attributes JavaDoc atts) throws SAXException JavaDoc {
149             switch (this.state) {
150                 case 0:
151                     // <i18n:catalogue>
152
if (!"".equals(ns) && !NS.equals(ns)) {
153                         throw new SAXException JavaDoc("Root element <" + EL_CATALOGUE +
154                                                "> must be non-namespaced or in i18n namespace.");
155                     }
156                     if (!EL_CATALOGUE.equals(localName)) {
157                         throw new SAXException JavaDoc("Root element must be <" + EL_CATALOGUE + ">.");
158                     }
159                     this.namespace = ns;
160                     this.state++;
161                     break;
162
163                 case 1:
164                     // <i18n:message>
165
if (!EL_MESSAGE.equals(localName)) {
166                         throw new SAXException JavaDoc("<" + EL_CATALOGUE + "> must contain <" +
167                                                EL_MESSAGE + "> elements only.");
168                     }
169                     if (!this.namespace.equals(ns)) {
170                         throw new SAXException JavaDoc("<" + EL_MESSAGE + "> element must be in '" +
171                                                this.namespace + "' namespace.");
172                     }
173                     String JavaDoc key = atts.getValue(AT_KEY);
174                     if (key == null) {
175                         throw new SAXException JavaDoc("<" + EL_MESSAGE + "> must have '" +
176                                                AT_KEY + "' attribute.");
177                     }
178                     this.buffer = new ParamSaxBuffer();
179                     this.values.put(key, this.buffer);
180                     this.state++;
181                     break;
182
183                 case 2:
184                     this.buffer.startElement(ns, localName, qName, atts);
185                     break;
186
187                 default:
188                     throw new SAXException JavaDoc("Internal error: Invalid state");
189             }
190         }
191
192         public void endElement(String JavaDoc ns, String JavaDoc localName, String JavaDoc qName) throws SAXException JavaDoc {
193             switch (this.state) {
194                 case 0:
195                     break;
196
197                 case 1:
198                     // </i18n:catalogue>
199
this.state--;
200                     break;
201
202                 case 2:
203                     if (this.namespace.equals(ns) && EL_MESSAGE.equals(localName)) {
204                         // </i18n:message>
205
this.buffer = null;
206                         this.state--;
207                     } else {
208                         this.buffer.endElement(ns, localName, qName);
209                     }
210                     break;
211
212                 default:
213                     throw new SAXException JavaDoc("Internal error: Invalid state");
214             }
215         }
216
217         public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri) throws SAXException JavaDoc {
218             if (this.buffer != null) {
219                 this.buffer.startPrefixMapping(prefix, uri);
220             }
221         }
222
223         public void endPrefixMapping(String JavaDoc prefix) throws SAXException JavaDoc {
224             if (this.buffer != null) {
225                 this.buffer.endPrefixMapping(prefix);
226             }
227         }
228
229         public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException JavaDoc {
230             if (this.buffer != null) {
231                 this.buffer.ignorableWhitespace(ch, start, length);
232             }
233         }
234
235         public void characters(char[] ch, int start, int length) throws SAXException JavaDoc {
236             if (this.buffer != null) {
237                 this.buffer.characters(ch, start, length);
238             }
239         }
240     }
241
242
243     /**
244      * Construct a bundle.
245      * @param sourceURI source URI of the XML bundle
246      * @param locale locale
247      * @param parent parent bundle of this bundle
248      */

249     public XMLResourceBundle(String JavaDoc sourceURI, Locale JavaDoc locale, Bundle parent) {
250         this.sourceURI = sourceURI;
251         this.locale = locale;
252         this.parent = parent;
253         this.values = Collections.EMPTY_MAP;
254     }
255
256     /**
257      * (Re)Loads the XML bundle if necessary, based on the source URI.
258      * @return true if reloaded successfully
259      */

260     protected boolean reload(SourceResolver resolver, long interval) {
261         Source newSource = null;
262         Map JavaDoc newValues;
263
264         try {
265             int valid = this.validity == null ? SourceValidity.INVALID : this.validity.isValid();
266             if (valid != SourceValidity.VALID) {
267                 // Saved validity is not valid, get new source and validity
268
newSource = resolver.resolveURI(this.sourceURI);
269                 SourceValidity newValidity = newSource.getValidity();
270
271                 if (valid == SourceValidity.INVALID || this.validity.isValid(newValidity) != SourceValidity.VALID) {
272                     newValues = new HashMap JavaDoc();
273                     SourceUtil.toSAX(newSource, new SAXContentHandler(newValues));
274                     synchronized (this) {
275                         // Update source validity and values
276
if (interval > 0 && newValidity != null) {
277                             this.validity = new DelayedValidity(interval, newValidity);
278                         } else {
279                             this.validity = newValidity;
280                         }
281                         this.values = newValues;
282                     }
283                 }
284             }
285
286             // Success
287
return true;
288
289         } catch (MalformedURLException JavaDoc e) {
290             getLogger().error("Bundle <" + this.sourceURI + "> not loaded: Invalid URI", e);
291             newValues = Collections.EMPTY_MAP;
292
293         } catch (ResourceNotFoundException e) {
294             // FIXME: this damn SourceUtil converts SNFE to RNFE!!!
295
if (getLogger().isInfoEnabled()) {
296                 if (newSource != null && !newSource.exists()) {
297                     // Nominal case where a bundle doesn't exist: log the message but not the exception
298
getLogger().info("Bundle <" + sourceURI + "> not loaded: Source URI not found");
299                 } else {
300                     // Log the exception
301
getLogger().info("Bundle <" + sourceURI + "> not loaded: Source URI not found", e);
302                 }
303             }
304             newValues = Collections.EMPTY_MAP;
305
306         } catch (SourceNotFoundException e) {
307             if (getLogger().isInfoEnabled()) {
308                 if (newSource != null && !newSource.exists()) {
309                     // Nominal case where a bundle doesn't exist: log the message but not the exception
310
getLogger().info("Bundle <" + sourceURI + "> not loaded: Source URI not found");
311                 } else {
312                     // Log the exception
313
getLogger().info("Bundle <" + sourceURI + "> not loaded: Source URI not found", e);
314                 }
315             }
316             newValues = Collections.EMPTY_MAP;
317
318         } catch (SAXException JavaDoc e) {
319             getLogger().error("Bundle <" + sourceURI + "> not loaded: Invalid XML", e);
320             // Keep existing loaded values
321
newValues = this.values;
322
323         } catch (Exception JavaDoc e) {
324             getLogger().error("Bundle <" + sourceURI + "> not loaded: Exception", e);
325             // Keep existing loaded values
326
newValues = this.values;
327
328         } finally {
329             if (newSource != null) {
330                 resolver.release(newSource);
331             }
332         }
333
334         synchronized (this) {
335             // Use expires validity to delay next reloading.
336
if (interval > 0) {
337                 this.validity = new ExpiresValidity(interval);
338             } else {
339                 this.validity = null;
340             }
341             this.values = newValues;
342         }
343
344         // Failure
345
return false;
346     }
347
348     /**
349      * Gets the locale of the bundle.
350      *
351      * @return the locale
352      */

353     public Locale JavaDoc getLocale() {
354         return this.locale;
355     }
356
357     /**
358      * Gets the source URI of the bundle.
359      *
360      * @return the source URI
361      */

362     public String JavaDoc getSourceURI() {
363         return this.sourceURI;
364     }
365
366     /**
367      * Gets the validity of the bundle.
368      *
369      * @return the validity
370      */

371     public SourceValidity getValidity() {
372         return this.validity;
373     }
374
375     /**
376      * Get an instance of the {@link ParamSaxBuffer} associated with the key.
377      *
378      * @param key the key
379      * @return the value, or null if no value associated with the key.
380      */

381     public Object JavaDoc getObject(String JavaDoc key) {
382         if (key == null) {
383             return null;
384         }
385
386         Object JavaDoc value = this.values.get(key);
387         if (value != null) {
388             return value;
389         }
390
391         if (this.parent != null) {
392             return this.parent.getObject(key);
393         }
394
395         return null;
396     }
397
398     /**
399      * Get a string representation of the value object by key.
400      *
401      * @param key the key
402      * @return the string value, or null if no value associated with the key.
403      */

404     public String JavaDoc getString(String JavaDoc key) {
405         if (key == null) {
406             return null;
407         }
408
409         Object JavaDoc value = this.values.get(key);
410         if (value != null) {
411             return value.toString();
412         }
413
414         if (this.parent != null) {
415             return this.parent.getString(key);
416         }
417
418         return null;
419     }
420 }
421
Popular Tags