View Javadoc
1   /*
2    * Copyright 2007 Joachim Grueneis
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package org.castor.xml;
15  
16  import java.beans.PropertyChangeListener;
17  import java.beans.PropertyChangeSupport;
18  import java.io.IOException;
19  import java.io.OutputStream;
20  import java.io.Writer;
21  
22  import javax.inject.Inject;
23  import javax.xml.parsers.SAXParser;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.castor.core.util.Messages;
28  import org.castor.mapping.BindingType;
29  import org.castor.mapping.MappingUnmarshaller;
30  import org.exolab.castor.mapping.Mapping;
31  import org.exolab.castor.mapping.MappingException;
32  import org.exolab.castor.mapping.MappingLoader;
33  import org.exolab.castor.util.RegExpEvaluator;
34  import org.exolab.castor.xml.AbstractXMLNaming;
35  import org.exolab.castor.xml.Introspector;
36  import org.exolab.castor.xml.NodeType;
37  import org.exolab.castor.xml.OutputFormat;
38  import org.exolab.castor.xml.ResolverException;
39  import org.exolab.castor.xml.Serializer;
40  import org.exolab.castor.xml.XMLClassDescriptor;
41  import org.exolab.castor.xml.XMLClassDescriptorResolver;
42  import org.exolab.castor.xml.XMLContext;
43  import org.exolab.castor.xml.XMLSerializerFactory;
44  import org.exolab.castor.xml.util.DefaultNaming;
45  import org.exolab.castor.xml.util.ResolverStrategy;
46  import org.exolab.castor.xml.util.XMLParserUtils;
47  import org.xml.sax.DocumentHandler;
48  import org.xml.sax.Parser;
49  import org.xml.sax.SAXException;
50  import org.xml.sax.XMLReader;
51  
52  /**
53   * The internal context is meant as center piece providing (and keeping) all information that is
54   * required by Marshaller, Unmarshaller, SourceGenerator, MappingTool, SchemaReader and
55   * SchemaWriter. It is created, filled with initial data and put into all other parts of Castor by
56   * {@link XMLContext}. It is NOT meant to be directly instantiated by user implementations! For all
57   * other objects it provides access to Castor state information (e.g. known descriptors) and
58   * configuration values.
59   * 
60   * @author <a href="mailto:jgrueneis At gmail DOT com">Joachim Grueneis</a>
61   * @since 1.1.2
62   */
63  public abstract class AbstractInternalContext implements InternalContext {
64  
65    private static final Log LOG = LogFactory.getFactory().getInstance(AbstractInternalContext.class);
66  
67    private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
68  
69    /**
70     * The properties to use internally to provide parser, serializer, ...
71     */
72    private org.castor.core.util.AbstractProperties _properties;
73  
74    /**
75     * {@link XMLClassDescriptorResolver} instance used for caching XML-related class descriptors.
76     */
77    private XMLClassDescriptorResolver _xmlClassDescriptorResolver;
78  
79    /**
80     * The XMLContext knows the one {@link Introspector} to be used.
81     */
82    private Introspector _introspector;
83  
84    /**
85     * The {@link XMLClassDescriptor} resolver strategy to use.
86     */
87    private ResolverStrategy _resolverStrategy;
88  
89    /**
90     * The {@link MappingLoader} to use.
91     */
92    private MappingLoader _mappingLoader;
93  
94    /**
95     * The {@link AbstractXMLNaming} to be used.
96     */
97    private XMLNaming _xmlNaming;
98  
99    /**
100    * The {@link JavaNaming} to be used.
101    */
102   @Inject
103   private JavaNaming _javaNaming;
104 
105   /**
106    * The {@link ClassLoader} to use.
107    */
108   private ClassLoader _classLoader;
109 
110   /**
111    * The {@link NodeType} to use for primitives.
112    */
113   private NodeType _primitiveNodeType;
114 
115   /**
116    * The {@link RegExpevaluator} to use.
117    */
118   private RegExpEvaluator _regExpEvaluator;
119 
120   public AbstractInternalContext() {
121     _properties = XMLProperties.newInstance();
122     // TODO[WG]: remove once injection works
123     _javaNaming = new JavaNamingImpl(this);
124   }
125 
126   @Override
127   public void addMapping(final Mapping mapping) throws MappingException {
128     MappingUnmarshaller mappingUnmarshaller = new MappingUnmarshaller();
129     MappingLoader mappingLoader = mappingUnmarshaller.getMappingLoader(mapping, BindingType.XML);
130     _xmlClassDescriptorResolver.setMappingLoader(mappingLoader);
131   }
132 
133   @Override
134   public void addClass(final Class<?> clazz) throws ResolverException {
135     _xmlClassDescriptorResolver.addClass(clazz);
136   }
137 
138   @Override
139   public void addClasses(final Class<?>[] clazzes) throws ResolverException {
140     _xmlClassDescriptorResolver.addClasses(clazzes);
141   }
142 
143   @Override
144   public void addPackage(final String packageName) throws ResolverException {
145     _xmlClassDescriptorResolver.addPackage(packageName);
146   }
147 
148   @Override
149   public void addPackages(final String[] packageNames) throws ResolverException {
150     _xmlClassDescriptorResolver.addPackages(packageNames);
151   }
152 
153   @Override
154   public void setResolver(final XMLClassDescriptorResolver xmlClassDescriptorResolver) {
155     this._xmlClassDescriptorResolver = xmlClassDescriptorResolver;
156   }
157 
158   @Override
159   public void setProperty(final String propertyName, final Object value) {
160     // resetting all values that are only reinitialized if null
161     if (propertyName == null) {
162       IllegalArgumentException iae = new IllegalArgumentException(
163           "setProperty must not be called with a propertyName == null");
164       LOG.warn(iae.getMessage());
165       throw iae;
166     }
167     if (propertyName.equals(XMLProperties.XML_NAMING)) {
168       if (value instanceof String) {
169         setXMLNaming((String) value);
170       } else if (value instanceof XMLNaming) {
171         setXMLNaming((XMLNaming) value);
172       } else {
173         IllegalArgumentException iae = new IllegalArgumentException(
174             "XML Naming can only be set to a String or an implementation of XMLNaming");
175         LOG.warn(iae.getMessage());
176         throw iae;
177       }
178     }
179     if (propertyName.equals(XMLProperties.JAVA_NAMING)) {
180       if (value instanceof String) {
181         setJavaNaming((String) value);
182       } else if (value instanceof JavaNaming) {
183         setJavaNaming((JavaNaming) value);
184       } else {
185         IllegalArgumentException iae = new IllegalArgumentException(
186             "Java Naming can only be set to a String or an implementation of JavaNaming");
187         LOG.warn(iae.getMessage());
188         throw iae;
189       }
190     }
191     _primitiveNodeType = null;
192     _regExpEvaluator = null;
193 
194     // now writing the new property
195     this.setPropertyInternal(propertyName, value);
196   }
197 
198   @Override
199   public Object getProperty(final String propertyName) {
200     return _properties.getObject(propertyName);
201   }
202 
203   @Override
204   public XMLNaming getXMLNaming() {
205     if (_xmlNaming != null) {
206       return _xmlNaming;
207     }
208 
209     String prop = _properties.getString(XMLProperties.XML_NAMING, null);
210     setXMLNaming(prop);
211     return _xmlNaming;
212   }
213 
214   /**
215    * @deprecated Makes no sence!
216    */
217   @Override
218   public XMLNaming getXMLNaming(final ClassLoader classLoader) {
219     return getXMLNaming();
220   } // -- getXMLNaming
221 
222   @Override
223   public JavaNaming getJavaNaming() {
224     return _javaNaming;
225   }
226 
227   @Override
228   public Parser getParser() {
229     return getParser(null);
230   }
231 
232   @Override
233   public Parser getParser(final String features) {
234     return XMLParserUtils.getParser(_properties, features);
235   }
236 
237   @Override
238   public XMLReader getXMLReader() {
239     return getXMLReader(null);
240   }
241 
242   @Override
243   public XMLReader getXMLReader(final String features) {
244     XMLReader reader = null;
245     Boolean validation = _properties.getBoolean(XMLProperties.PARSER_VALIDATION);
246     Boolean namespaces = _properties.getBoolean(XMLProperties.NAMESPACES);
247 
248     String readerClassName = _properties.getString(XMLProperties.PARSER);
249 
250     if (readerClassName == null || readerClassName.length() == 0) {
251       SAXParser saxParser =
252           XMLParserUtils.getSAXParser(validation.booleanValue(), namespaces.booleanValue());
253       if (saxParser != null) {
254         try {
255           reader = saxParser.getXMLReader();
256         } catch (SAXException e) {
257           LOG.error(Messages.format("conf.configurationError", e));
258         }
259       }
260     }
261 
262     if (reader == null) {
263       if ((readerClassName == null) || (readerClassName.length() == 0)
264           || (readerClassName.equalsIgnoreCase("xerces"))) {
265         readerClassName = "org.apache.xerces.parsers.SAXParser";
266       }
267 
268       reader = XMLParserUtils.instantiateXMLReader(readerClassName);
269     }
270 
271     XMLParserUtils.setFeaturesOnXmlReader(
272         _properties.getString(XMLProperties.PARSER_FEATURES, features),
273         _properties.getString(XMLProperties.PARSER_FEATURES_DISABLED, ""),
274         validation.booleanValue(), namespaces.booleanValue(), reader);
275 
276     return reader;
277 
278   }
279 
280   @Override
281   public NodeType getPrimitiveNodeType() {
282 
283     if (_primitiveNodeType != null) {
284       return _primitiveNodeType;
285     }
286 
287     String prop = _properties.getString(XMLProperties.PRIMITIVE_NODE_TYPE, null);
288     if (prop == null) {
289       return null;
290     }
291     _primitiveNodeType = NodeType.getNodeType(prop);
292     return _primitiveNodeType;
293   }
294 
295   @Override
296   public RegExpEvaluator getRegExpEvaluator() {
297     if (_regExpEvaluator != null) {
298       return _regExpEvaluator;
299     }
300 
301     String className = _properties.getString(XMLProperties.REG_EXP_CLASS_NAME, "");
302     if (className.length() == 0) {
303       _regExpEvaluator = null;
304     } else {
305       try {
306         Class<?> regExpEvalClass = Class.forName(className);
307         _regExpEvaluator = (RegExpEvaluator) regExpEvalClass.newInstance();
308       } catch (ClassNotFoundException e) {
309         throw new RuntimeException(Messages.format("conf.failedInstantiateRegExp", className, e));
310       } catch (InstantiationException e) {
311         throw new RuntimeException(Messages.format("conf.failedInstantiateRegExp", className, e));
312       } catch (IllegalAccessException e) {
313         throw new RuntimeException(Messages.format("conf.failedInstantiateRegExp", className, e));
314       }
315     }
316     return _regExpEvaluator;
317   }
318 
319   @Override
320   public Serializer getSerializer() {
321     return XMLParserUtils.getSerializer(_properties);
322   }
323 
324   @Override
325   public OutputFormat getOutputFormat() {
326     return XMLParserUtils.getOutputFormat(_properties);
327   }
328 
329   /**
330    * Returns the currently configured XMLSerializerFactory instance.
331    * 
332    * @param serializerFactoryName the class name of the serializer factory
333    * @return XMLSerializerFactory to use by Castor
334    */
335   protected XMLSerializerFactory getSerializerFactory(final String serializerFactoryName) {
336     return XMLParserUtils.getSerializerFactory(serializerFactoryName);
337   }
338 
339   @Override
340   public DocumentHandler getSerializer(final OutputStream output) throws IOException {
341     Serializer serializer;
342     DocumentHandler docHandler;
343 
344     serializer = getSerializer();
345     serializer.setOutputByteStream(output);
346     docHandler = serializer.asDocumentHandler();
347     if (docHandler == null) {
348       throw new RuntimeException(
349           Messages.format("conf.serializerNotSaxCapable", serializer.getClass().getName()));
350     }
351     return docHandler;
352   }
353 
354   @Override
355   public DocumentHandler getSerializer(final Writer output) throws IOException {
356     Serializer serializer;
357     DocumentHandler docHandler;
358 
359     serializer = getSerializer();
360     serializer.setOutputCharStream(output);
361     docHandler = serializer.asDocumentHandler();
362     if (docHandler == null) {
363       throw new RuntimeException(
364           Messages.format("conf.serializerNotSaxCapable", serializer.getClass().getName()));
365     }
366     return docHandler;
367   }
368 
369   @Override
370   public XMLClassDescriptorResolver getXMLClassDescriptorResolver() {
371     return _xmlClassDescriptorResolver;
372   }
373 
374   @Override
375   public Introspector getIntrospector() {
376     return _introspector;
377   }
378 
379   @Override
380   public ResolverStrategy getResolverStrategy() {
381     return _resolverStrategy;
382   }
383 
384   @Override
385   public void setResolverStrategy(final ResolverStrategy resolverStrategy) {
386     _resolverStrategy = resolverStrategy;
387   }
388 
389   @Override
390   public void setMappingLoader(final MappingLoader mappingLoader) {
391     _mappingLoader = mappingLoader;
392   }
393 
394   @Override
395   public MappingLoader getMappingLoader() {
396     return _mappingLoader;
397   }
398 
399   @Override
400   public void setJavaNaming(final JavaNaming javaNaming) {
401     _javaNaming = javaNaming;
402   }
403 
404   public void setJavaNaming(final String javaNamingProperty) {
405     if (javaNamingProperty == null || javaNamingProperty.length() == 0) {
406       _javaNaming = new JavaNamingImpl(this);
407     } else {
408       try {
409         Class<JavaNaming> cls = (Class<JavaNaming>) Class.forName(javaNamingProperty);
410         _javaNaming = cls.newInstance();
411       } catch (Exception e) {
412         IllegalArgumentException iae =
413             new IllegalArgumentException("Failed to load JavaNaming: " + e);
414         LOG.warn(iae.getMessage());
415         throw iae;
416       }
417     }
418   }
419 
420   @Override
421   public void setXMLNaming(final XMLNaming xmlNaming) {
422     _xmlNaming = xmlNaming;
423     // propagate to e.g. Introspector also!!
424     if (_introspector != null) {
425       _introspector.setNaming(_xmlNaming);
426     }
427   }
428 
429   /**
430    * This XMLNaming setter is meant to be used when working in property style instead of setting an
431    * XMLNaming implementation.
432    * 
433    * @param xmlNamingProperty to set the XMLNaming property as read from configuration
434    */
435   public void setXMLNaming(final String xmlNamingProperty) {
436     if ((xmlNamingProperty == null) || (xmlNamingProperty.equalsIgnoreCase("lower"))) {
437       setXMLNaming(new DefaultNaming());
438     } else if (xmlNamingProperty.equalsIgnoreCase("mixed")) {
439       DefaultNaming dn = new DefaultNaming();
440       dn.setStyle(DefaultNaming.MIXED_CASE_STYLE);
441       setXMLNaming(dn);
442     } else {
443       try {
444         Class<XMLNaming> cls = (Class<XMLNaming>) Class.forName(xmlNamingProperty);
445         setXMLNaming(cls.newInstance());
446       } catch (Exception e) {
447         IllegalArgumentException iae =
448             new IllegalArgumentException("Failed to load XMLNaming: " + e);
449         LOG.warn(iae.getMessage());
450         throw iae;
451       }
452     }
453     if (_xmlNaming == null) {
454       IllegalArgumentException iae = new IllegalArgumentException(
455           "Failed to correctly set XMLNaming; property was: " + xmlNamingProperty);
456       LOG.warn(iae.getMessage());
457       throw iae;
458     }
459   }
460 
461   @Override
462   public void setProperty(final String propertyName, final boolean value) {
463     this.setPropertyInternal(propertyName, Boolean.valueOf(value));
464   }
465 
466   private void setPropertyInternal(final String propertyName, final Object value) {
467     Object oldValue = this._properties.getObject(propertyName);
468 
469     if (oldValue == null) {
470       if (value != null) {
471         this._properties.put(propertyName, value);
472         this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, value);
473       }
474     } else {
475       if (!oldValue.equals(value)) {
476         this._properties.put(propertyName, value);
477         this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, value);
478       }
479     }
480   }
481 
482   @Override
483   public Boolean getBooleanProperty(final String propertyName) {
484     return _properties.getBoolean(propertyName);
485   }
486 
487   @Override
488   public String getStringProperty(final String propertyName) {
489     return _properties.getString(propertyName);
490   }
491 
492   @Override
493   public void setClassLoader(final ClassLoader classLoader) {
494     _classLoader = classLoader;
495     if (_xmlClassDescriptorResolver != null) {
496       _xmlClassDescriptorResolver.setClassLoader(classLoader);
497     }
498   }
499 
500   @Override
501   public void setXMLClassDescriptorResolver(
502       final XMLClassDescriptorResolver xmlClassDescriptorResolver) {
503     _xmlClassDescriptorResolver = xmlClassDescriptorResolver;
504   }
505 
506   @Override
507   public void setIntrospector(final Introspector introspector) {
508     _introspector = introspector;
509   }
510 
511   @Override
512   public ClassLoader getClassLoader() {
513     return _classLoader;
514   }
515 
516   @Override
517   public boolean getLenientIdValidation() {
518     Boolean lenientIdValidation = _properties.getBoolean(XMLProperties.LENIENT_ID_VALIDATION);
519     if (lenientIdValidation == null) {
520       String message = "Property lenientIdValidation must not be null";
521       LOG.warn(message);
522       throw new IllegalStateException(message);
523     }
524     return lenientIdValidation.booleanValue();
525   }
526 
527   @Override
528   public boolean getLenientSequenceOrder() {
529     Boolean lenientSequenceOrder = _properties.getBoolean(XMLProperties.LENIENT_SEQUENCE_ORDER);
530     if (lenientSequenceOrder == null) {
531       String message = "Property lenientSequenceOrder must not be null";
532       LOG.warn(message);
533       throw new IllegalStateException(message);
534     }
535     return lenientSequenceOrder.booleanValue();
536   }
537 
538   @Override
539   public Boolean getLoadPackageMapping() {
540     return _properties.getBoolean(XMLProperties.LOAD_PACKAGE_MAPPING);
541   }
542 
543   @Override
544   public void setLoadPackageMapping(final Boolean loadPackageMapping) {
545     _properties.put(XMLProperties.LOAD_PACKAGE_MAPPING, loadPackageMapping);
546   }
547 
548   @Override
549   public Boolean getUseIntrospector() {
550     return _properties.getBoolean(XMLProperties.USE_INTROSPECTION);
551   }
552 
553   @Override
554   public void setUseIntrospector(final Boolean useIntrospector) {
555     _properties.put(XMLProperties.USE_INTROSPECTION, useIntrospector);
556   }
557 
558   @Override
559   public boolean marshallingValidation() {
560     Boolean marshallingValidation = _properties.getBoolean(XMLProperties.MARSHALLING_VALIDATION);
561     if (marshallingValidation == null) {
562       String message = "Property marshallingValidation must not be null";
563       LOG.warn(message);
564       throw new IllegalStateException(message);
565     }
566     return marshallingValidation.booleanValue();
567   }
568 
569   @Override
570   public boolean strictElements() {
571     Boolean strictElements = _properties.getBoolean(XMLProperties.STRICT_ELEMENTS);
572     if (strictElements == null) {
573       String message = "Property strictElements must not be null";
574       LOG.warn(message);
575       throw new IllegalStateException(message);
576     }
577     return strictElements.booleanValue();
578   }
579 
580   @Override
581   public void addPropertyChangeListener(PropertyChangeListener listener) {
582     this.propertyChangeSupport.addPropertyChangeListener(listener);
583   }
584 
585   @Override
586   public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
587     this.propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
588   }
589 
590   @Override
591   public void removePropertyChangeListener(PropertyChangeListener listener) {
592     this.propertyChangeSupport.removePropertyChangeListener(listener);
593   }
594 
595   @Override
596   public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
597     this.propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
598   }
599 }