1 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 ; 31 import org.xml.sax.ContentHandler ; 32 import org.xml.sax.Locator ; 33 import org.xml.sax.SAXException ; 34 35 import java.net.MalformedURLException ; 36 import java.util.Collections ; 37 import java.util.HashMap ; 38 import java.util.Locale ; 39 import java.util.Map ; 40 41 66 public class XMLResourceBundle extends AbstractLogEnabled 67 implements Bundle { 68 69 72 public static final String NS = "http://apache.org/cocoon/i18n/2.0"; 73 74 77 public static final String EL_CATALOGUE = "catalogue"; 78 79 82 public static final String EL_MESSAGE = "message"; 83 84 87 public static final String AT_KEY = "key"; 88 89 92 private String sourceURI; 93 94 97 private SourceValidity validity; 98 99 102 private Locale locale; 103 104 107 protected Bundle parent; 108 109 112 protected Map values; 113 114 115 118 private static class SAXContentHandler implements ContentHandler { 119 private Map values; 120 private int state; 121 private String namespace; 122 private ParamSaxBuffer buffer; 123 124 public SAXContentHandler(Map values) { 125 this.values = values; 126 } 127 128 public void setDocumentLocator(Locator arg0) { 129 } 131 132 public void startDocument() throws SAXException { 133 } 135 136 public void endDocument() throws SAXException { 137 } 139 140 public void processingInstruction(String arg0, String arg1) throws SAXException { 141 } 143 144 public void skippedEntity(String arg0) throws SAXException { 145 } 147 148 public void startElement(String ns, String localName, String qName, Attributes atts) throws SAXException { 149 switch (this.state) { 150 case 0: 151 if (!"".equals(ns) && !NS.equals(ns)) { 153 throw new SAXException ("Root element <" + EL_CATALOGUE + 154 "> must be non-namespaced or in i18n namespace."); 155 } 156 if (!EL_CATALOGUE.equals(localName)) { 157 throw new SAXException ("Root element must be <" + EL_CATALOGUE + ">."); 158 } 159 this.namespace = ns; 160 this.state++; 161 break; 162 163 case 1: 164 if (!EL_MESSAGE.equals(localName)) { 166 throw new SAXException ("<" + EL_CATALOGUE + "> must contain <" + 167 EL_MESSAGE + "> elements only."); 168 } 169 if (!this.namespace.equals(ns)) { 170 throw new SAXException ("<" + EL_MESSAGE + "> element must be in '" + 171 this.namespace + "' namespace."); 172 } 173 String key = atts.getValue(AT_KEY); 174 if (key == null) { 175 throw new SAXException ("<" + 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 ("Internal error: Invalid state"); 189 } 190 } 191 192 public void endElement(String ns, String localName, String qName) throws SAXException { 193 switch (this.state) { 194 case 0: 195 break; 196 197 case 1: 198 this.state--; 200 break; 201 202 case 2: 203 if (this.namespace.equals(ns) && EL_MESSAGE.equals(localName)) { 204 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 ("Internal error: Invalid state"); 214 } 215 } 216 217 public void startPrefixMapping(String prefix, String uri) throws SAXException { 218 if (this.buffer != null) { 219 this.buffer.startPrefixMapping(prefix, uri); 220 } 221 } 222 223 public void endPrefixMapping(String prefix) throws SAXException { 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 { 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 { 236 if (this.buffer != null) { 237 this.buffer.characters(ch, start, length); 238 } 239 } 240 } 241 242 243 249 public XMLResourceBundle(String sourceURI, Locale locale, Bundle parent) { 250 this.sourceURI = sourceURI; 251 this.locale = locale; 252 this.parent = parent; 253 this.values = Collections.EMPTY_MAP; 254 } 255 256 260 protected boolean reload(SourceResolver resolver, long interval) { 261 Source newSource = null; 262 Map newValues; 263 264 try { 265 int valid = this.validity == null ? SourceValidity.INVALID : this.validity.isValid(); 266 if (valid != SourceValidity.VALID) { 267 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 (); 273 SourceUtil.toSAX(newSource, new SAXContentHandler(newValues)); 274 synchronized (this) { 275 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 return true; 288 289 } catch (MalformedURLException e) { 290 getLogger().error("Bundle <" + this.sourceURI + "> not loaded: Invalid URI", e); 291 newValues = Collections.EMPTY_MAP; 292 293 } catch (ResourceNotFoundException e) { 294 if (getLogger().isInfoEnabled()) { 296 if (newSource != null && !newSource.exists()) { 297 getLogger().info("Bundle <" + sourceURI + "> not loaded: Source URI not found"); 299 } else { 300 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 getLogger().info("Bundle <" + sourceURI + "> not loaded: Source URI not found"); 311 } else { 312 getLogger().info("Bundle <" + sourceURI + "> not loaded: Source URI not found", e); 314 } 315 } 316 newValues = Collections.EMPTY_MAP; 317 318 } catch (SAXException e) { 319 getLogger().error("Bundle <" + sourceURI + "> not loaded: Invalid XML", e); 320 newValues = this.values; 322 323 } catch (Exception e) { 324 getLogger().error("Bundle <" + sourceURI + "> not loaded: Exception", e); 325 newValues = this.values; 327 328 } finally { 329 if (newSource != null) { 330 resolver.release(newSource); 331 } 332 } 333 334 synchronized (this) { 335 if (interval > 0) { 337 this.validity = new ExpiresValidity(interval); 338 } else { 339 this.validity = null; 340 } 341 this.values = newValues; 342 } 343 344 return false; 346 } 347 348 353 public Locale getLocale() { 354 return this.locale; 355 } 356 357 362 public String getSourceURI() { 363 return this.sourceURI; 364 } 365 366 371 public SourceValidity getValidity() { 372 return this.validity; 373 } 374 375 381 public Object getObject(String key) { 382 if (key == null) { 383 return null; 384 } 385 386 Object 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 404 public String getString(String key) { 405 if (key == null) { 406 return null; 407 } 408 409 Object 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 |