View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
32   *
33   *
34   * $Id$
35   */
36  package org.exolab.castor.builder;
37  
38  // --Castor imports
39  import java.io.File;
40  import java.io.FileInputStream;
41  import java.io.IOException;
42  import java.io.InputStream;
43  import java.util.ArrayList;
44  import java.util.Enumeration;
45  import java.util.Hashtable;
46  import java.util.List;
47  import java.util.Properties;
48  import java.util.StringTokenizer;
49  
50  import org.apache.commons.logging.Log;
51  import org.apache.commons.logging.LogFactory;
52  import org.castor.core.util.AbstractProperties;
53  import org.castor.core.util.Messages;
54  import org.castor.xml.JavaNaming;
55  import org.castor.xml.JavaNamingImpl;
56  import org.castor.xml.XMLProperties;
57  
58  /**
59   * The configuration for the SourceGenerator. HACK this class is a configuration but does not base
60   * on Castor Configuration! Raised CASTOR-2195 to solve this issue.
61   *
62   * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
63   * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
64   * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
65   */
66  public class BuilderConfiguration {
67    /**
68     * The <a href="http://jakarta.apache.org/commons/logging/">Jakarta Commons Logging</a> instance
69     * used for all logging.
70     */
71    private static final Log LOG = LogFactory.getLog(BuilderConfiguration.class);
72  
73    /** String representation of directory self pointer. */
74    private static final String SELF_DIRECTORY = "./";
75  
76    /** Length of string representation of directory self pointer. */
77    private static final int SELF_DIRECTORY_LENGTH = SELF_DIRECTORY.length();
78  
79    /** String representation of directory parent pointer. */
80    private static final String PARENT_DIRECTORY = "../";
81  
82    /** Length of string representation of directory parent pointer. */
83    private static final int PARENT_DIRECTORY_LENGTH = PARENT_DIRECTORY.length();
84  
85    /**
86     * Names of properties used in the configuration file.
87     */
88    public static class Property {
89      /**
90       * Property specifying whether or not to generate source code for bound properties. Currently
91       * all properties will be treated as bound properties if this flag is set to true. A value of
92       * 'true' enables bound properties.
93       *
94       * <pre>
95       * org.exolab.castor.builder.boundproperties
96       * </pre>
97       */
98      public static final String BOUND_PROPERTIES = "org.exolab.castor.builder.boundproperties";
99  
100     /**
101      * Property specifying whether to implement EnumeratedTypeAccess interface for all generated
102      * enumerated type classes.
103      *
104      * <pre>
105      * org.exolab.castor.builder.enumTypeAccessInterface
106      * </pre>
107      */
108     public static final String ENUM_TYPE_ACCESS_INTERFACE =
109         "org.exolab.castor.builder.enumTypeAccessInterface";
110 
111     /**
112      * Property specifying whether or not to generate source code for extra collection methods.
113      *
114      * <pre>
115      * org.exolab.castor.builder.extraCollectionMethods
116      * </pre>
117      */
118     public static final String EXTRA_COLLECTION_METHODS =
119         "org.exolab.castor.builder.extraCollectionMethods";
120 
121     /**
122      * Property specifying the super class for all generated classes.
123      *
124      * <pre>
125      * org.exolab.castor.builder.superclass
126      * </pre>
127      */
128     public static final String SUPER_CLASS = "org.exolab.castor.builder.superclass";
129 
130     /**
131      * Property specifying how element's and type's are mapped into a Java class hierarchy by the
132      * Source Generator. The value must contain one of the following. 'element' outputs a Java class
133      * hierarchy based on element names used in the XML Schema. This is the default. 'type' outputs
134      * a Java class hierarchy based on the type information defined in the XML Schema.
135      *
136      * <pre>
137      * org.exolab.castor.builder.javaclassmapping
138      * </pre>
139      */
140     public static final String JAVA_CLASS_MAPPING = "org.exolab.castor.builder.javaclassmapping";
141 
142     /**
143      * Property listing mapping between XML namespaces and Java packages.
144      */
145     public static final String NAMESPACE_PACKAGES_OLD = "org.exolab.castor.builder.nspackages";
146 
147     /**
148      * Property listing mapping between XML namespaces and Java packages.
149      */
150     public static final String NAMESPACE_PACKAGES = "org.exolab.castor.xml.nspackages";
151 
152     /**
153      * Property specifying if we want to have the equals method generated for each generated class.
154      */
155     public static final String EQUALS_METHOD = "org.exolab.castor.builder.equalsmethod";
156 
157     /**
158      * Property specifying if we want to use Wrapper Objects instead of primitives (eg
159      * java.lang.Float instead of float).
160      */
161     public static final String WRAPPER = "org.exolab.castor.builder.primitivetowrapper";
162 
163     /**
164      * Property specifying if we want to have a 'public static final String' generated for each
165      * attribute and element name used within a class descriptor.
166      */
167     public static final String CLASS_DESC_FIELD_NAMES =
168         "org.exolab.castor.builder.classdescfieldnames";
169 
170     /**
171      * Property specifying whether the Java sources generated should be 1.4 or 5.0 compliant.
172      */
173     public static final String JAVA_VERSION = "org.exolab.castor.builder.javaVersion";
174 
175     /**
176      * Forces the code generator to create 'old' Java 1.4 enumeration classes instead of Java 5
177      * enums for &lt;simpleType&gt; enumerations, even in Java 5 mode.
178      */
179     public static final String FORCE_JAVA4_ENUMS = "org.exolab.castor.builder.forceJava4Enums";
180 
181     /**
182      * The name of the configuration file.
183      *
184      * <pre>
185      * castor.properties
186      * </pre>
187      */
188     public static final String CONFIG_FILENAME_PROPERTY = "castorbuilder.properties";
189 
190     /**
191      * Maximum number of constant definitions within one file. This property is used to allow the
192      * user to configure the maximum number of constant definitions (within a Java class as
193      * generated as a result of an enumeration); default is 1000. Is this number if exceeded, no
194      * constants will be generated anymore.
195      *
196      * <pre>
197      * org.exolab.castor.builder.maxNumberOfConstants
198      * </pre>
199      */
200     public static final String MAX_CONSTANTS_PROPERTY =
201         "org.exolab.castor.builder.maxNumberOfConstants";
202 
203     /**
204      * Resource to load from the JAR file to load our defaults.
205      */
206     static final String RESOURCE_NAME = "/org/exolab/castor/builder/castorbuilder.properties";
207 
208     /**
209      * Registered class name conflict resolution strategies.
210      *
211      * <pre>
212      * org.exolab.castor.builder.nameConflictStrategies
213      * </pre>
214      */
215     public static final String NAME_CONFLICT_STRATEGIES =
216         "org.exolab.castor.builder.nameConflictStrategies";
217 
218     /**
219      * Property specifying whether automatic class name conflict resolution should be used or not;
220      * defaults to false.
221      *
222      * <pre>
223      * org.exolab.castor.builder.automaticConflictResolution
224      * </pre>
225      */
226     public static final String AUTOMATIC_CONFLICT_RESOLUTION =
227         "org.exolab.castor.builder.automaticConflictResolution";
228 
229     /**
230      * Property specifying the 'string' used in type strategy to be inserted between the actual
231      * element name and the type name (during automatic class name conflict resolution); defaults to
232      * 'By'.
233      *
234      * <pre>
235      * org.exolab.castor.builder.automaticConflictResolutionTypeSuffix
236      * </pre>
237      */
238     public static final String AUTOMATIC_CONFLICT_RESOLUTION_TYPE_SUFFIX =
239         "org.exolab.castor.builder.automaticConflictResolutionTypeSuffix";
240 
241     /**
242      * Property enlisting the supported {@link JClassPrinterFactory} instances available for
243      * creating {@link JClassPrinter} instances.
244      */
245     public static final String JCLASSPRINTER_FACTORIES =
246         "org.exolab.castor.builder.jclassPrinterFactories";
247 
248     /**
249      * Property specifying whether extra members/methods for extracting XML schema documentation
250      * should be made available; defaults to false.
251      * 
252      * <pre>
253      * org.exolab.castor.builder.extraDocumentationMethods = false
254      * </pre>
255      */
256     public static final String EXTRA_DOCUMENTATION_METHODS =
257         "org.exolab.castor.builder.extraDocumentationMethods";
258 
259     /**
260      * Property specifying whether cycle breaker code should be added to generated methods 'equals'
261      * and 'hashcode'; defaults to <i>true</i>.
262      * 
263      * <pre>
264      * org.exolab.castor.builder.useCycleBreaker = true
265      * </pre>
266      */
267     public static final String USE_CYCLE_BREAKER = "org.exolab.castor.builder.useCycleBreaker";
268 
269     /**
270      * Property specifying whether for Java field names the old naming conventions should be used;
271      * defaults to <i>true</i>.
272      * 
273      * <pre>
274      * org.exolab.castor.builder.field-naming.old = true
275      * </pre>
276      */
277     public static final String USE_OLD_FIELD_NAMING = "org.exolab.castor.builder.field-naming.old";
278 
279   } // --Property
280 
281   /**
282    * String value of false.
283    */
284   private static final String FALSE = "false";
285 
286   /**
287    * String value of true.
288    */
289   private static final String TRUE = "true";
290 
291   /**
292    * String value of element binding property.
293    */
294   private static final String ELEMENT_VALUE = "element";
295 
296   /**
297    * String value of type binding property.
298    */
299   private static final String TYPE_VALUE = "type";
300 
301   /**
302    * The default properties loaded from the configuration file.
303    */
304   private Properties _defaultProps = null;
305 
306   /**
307    * Our properties after all configuration has been loaded.
308    */
309   private Properties _localProps = null;
310 
311   /**
312    * Namespace URL to Java package mapping.
313    */
314   private Hashtable<String, String> _nspackages = new Hashtable<String, String>();
315 
316   /**
317    * schemaLocation to Java package mapping.
318    */
319   private Hashtable<String, String> _locpackages = new Hashtable<String, String>();
320 
321   /** JavaNaming to be used. */
322   private JavaNaming _javaNaming;
323 
324   /**
325    * hooks for (external) tools to add custom annotations to fields, classes and enumConstants
326    * during source generation.
327    */
328   private List<AnnotationBuilder> _annotationBuilders = new ArrayList<AnnotationBuilder>();
329 
330   // ------------------/
331 
332   /**
333    * Creates a default BuilderConfiguration.
334    */
335   public BuilderConfiguration() {
336     super();
337     getDefault();
338     _localProps = new Properties(_defaultProps);
339   } // -- BuilderConfiguration
340 
341   /**
342    * Returns the default configuration file. Changes to the returned properties set will affect all
343    * Castor functions relying on the default configuration.
344    *
345    * @return The default configuration
346    */
347   public final synchronized Properties getDefault() {
348     if (_defaultProps == null) {
349       load();
350     }
351     return _defaultProps;
352   } // -- getDefault
353 
354   /**
355    * Returns a property from the default configuration file. Equivalent to calling
356    * <tt>getProperty</tt> on the result of {@link #getDefault}.
357    *
358    * @param name The property name
359    * @param defValue The property's default value
360    * @return The property's value
361    */
362   public final String getProperty(final String name, final String defValue) {
363     return _localProps.getProperty(name, defValue);
364   } // -- getProperty
365 
366   /**
367    * Returns true if bound properties are enabled.
368    * <p>
369    * Enabling bound properties is controlled via the org.exolab.castor.builder.boundproperties item
370    * in the castorbuilder.properties file. The value is either 'true' or 'false'.
371    *
372    * @return true if bound properties are enabled.
373    */
374   public final boolean boundPropertiesEnabled() {
375     return TRUE.equalsIgnoreCase(_localProps.getProperty(Property.BOUND_PROPERTIES));
376   } // -- boundPropertiesEnabled
377 
378   /**
379    * Returns true if we generate an 'equals' method for each generated class.
380    * <p>
381    * Enabling this property is controlled via the org.exolab.castor.builder.equalsmethod item in the
382    * castorbuilder.properties file. The value is either 'true' or 'false'.
383    *
384    * @return true if bound properties are enabled.
385    */
386   public final boolean equalsMethod() {
387     return TRUE.equalsIgnoreCase(_localProps.getProperty(Property.EQUALS_METHOD));
388   } // -- equalsMethod
389 
390   /**
391    * Sets the 'equalsmethod' property.
392    *
393    * @param equals The value we want to use.
394    */
395   public final void setEqualsMethod(final boolean equals) {
396     String value = (equals) ? TRUE : FALSE;
397     _localProps.setProperty(Property.EQUALS_METHOD, value);
398   } // -- setEqualsMethod
399 
400   /**
401    * Returns true if we generate a 'public static final String' for the name of each attribute and
402    * element described by the class descriptor
403    * <p>
404    * Enabling this property is controlled via the org.exolab.castor.builder.classdescfieldnames item
405    * in the castorbuilder.properties file. The value is either 'true' or 'false'.
406    *
407    * @return true if bound properties are enabled.
408    */
409   public final boolean classDescFieldNames() {
410     return _localProps.getProperty(Property.CLASS_DESC_FIELD_NAMES, "").equalsIgnoreCase(TRUE);
411   } // -- classDescFieldNames
412 
413   /**
414    * Returns true if extra methods for collection fields should be generated. Such methods include
415    * set/get methods for the actual collection in addition to the array methods.
416    * <p>
417    * Enabling extra collection methods is controlled via the
418    * org.exolab.castor.builder.extraCollectionMethods property in the castorbuilder.properties file.
419    * The value is either 'true' or 'false'.
420    *
421    * @return true if extra collection methods are enabled.
422    */
423   public final boolean generateExtraCollectionMethods() {
424     return _localProps.getProperty(Property.EXTRA_COLLECTION_METHODS, "").equalsIgnoreCase(TRUE);
425   } // -- generateExtraCollectionMethods
426 
427   /**
428    * Sets the 'classDescFieldNames' property.
429    *
430    * @param classDescFieldNames the value we want to ues
431    */
432   public final void setClassDescFieldNames(final boolean classDescFieldNames) {
433     String value = (classDescFieldNames) ? TRUE : FALSE;
434     _localProps.setProperty(Property.CLASS_DESC_FIELD_NAMES, value);
435   } // -- setClassDescFieldNames
436 
437   /**
438    * Returns true if primitive types have to be used as Objects (eg. replacing <code>float</code> by
439    * <code>java.lang.Float</code>).
440    * 
441    * @return true if primitive types have to be used as Objects.
442    */
443   public final boolean usePrimitiveWrapper() {
444     return _localProps.getProperty(Property.WRAPPER, "").equalsIgnoreCase(TRUE);
445   } // -- usePrimitiveWrapper
446 
447   /**
448    * Sets the 'primitivetowrapper' property.
449    *
450    * @param wrapper the value we want to use.
451    */
452   public final void setPrimitiveWrapper(final boolean wrapper) {
453     String value = (wrapper) ? TRUE : FALSE;
454     _localProps.setProperty(Property.WRAPPER, value);
455   } // -- setPrimitiveWrapper
456 
457   /**
458    * Returns true if we generate the implements EnumeratedTypeAccess interface for enumerated type
459    * classes. The value is either 'true' or 'false'
460    *
461    * @return true if use enumerated type interface is enabled
462    */
463   public final boolean useEnumeratedTypeInterface() {
464     return TRUE.equalsIgnoreCase(_localProps.getProperty(Property.ENUM_TYPE_ACCESS_INTERFACE));
465   } // -- useEnumeratedTypeInterface
466 
467   /**
468    * Returns true if we generate the implements EnumeratedTypeAccess interface for enumerated type
469    * classes. The value is either 'true' or 'false'
470    *
471    * @return true if use enumerated type interface is enabled
472    */
473   public final boolean useJava50() {
474     return "5.0".equalsIgnoreCase(_localProps.getProperty(Property.JAVA_VERSION, "1.4"));
475   } // -- useEnumeratedTypeInterface
476 
477   /**
478    * Indicates what kind of enumeration should be created for &lt;xs:simpleType&gt; enumerations.
479    *
480    * @return true if Java 5 source code should be generated.
481    */
482   public final boolean useJava5Enums() {
483     return useJava50()
484         && FALSE.equalsIgnoreCase(_localProps.getProperty(Property.FORCE_JAVA4_ENUMS, FALSE));
485   }
486 
487   /**
488    * Add support to set java version programmatically.
489    */
490   public final void forceUseJava50() {
491     _localProps.setProperty(Property.JAVA_VERSION, "5.0");
492   }
493 
494   /**
495    * Returns true if extra methods for accessing XML schema documentation should be generated;
496    * default to 'false'.
497    *
498    * @return true if if extra methods for accessing XML schema documentation should be generated
499    */
500   public final boolean generateExtraDocumentationMethods() {
501     String extraDocumentationMethods =
502         _localProps.getProperty(Property.EXTRA_DOCUMENTATION_METHODS, "false");
503     return Boolean.parseBoolean(extraDocumentationMethods);
504   }
505 
506   /**
507    * Returns true if the class {@link CycleBreaker} should be used during code generation; defaults
508    * to 'true'.
509    *
510    * @return true if the class {@link CycleBreaker} should be used during code generation
511    */
512   public final boolean useCycleBreaker() {
513     return Boolean.valueOf(_localProps.getProperty(Property.USE_CYCLE_BREAKER, "true"));
514   }
515 
516   /**
517    * Returns the maximum number of static constant definitions that are acceptable within one class
518    * file; default is 1000.
519    *
520    * @return the maximum number of static constant definitions acceptable within one class file
521    */
522   public final int getMaximumNumberOfConstants() {
523     String property = _localProps.getProperty(Property.MAX_CONSTANTS_PROPERTY, "1000");
524     return Integer.parseInt(property);
525   }
526 
527   /**
528    * Sets the 'enumTypeAccessInterface' property.
529    *
530    * @param flag the value we want to use
531    */
532   public final void setUseEnumeratedTypeInterface(final boolean flag) {
533     String value = (flag) ? TRUE : FALSE;
534     _localProps.setProperty(Property.ENUM_TYPE_ACCESS_INTERFACE, value);
535   } // -- setUseEnumeratedTypeInterface
536 
537   /**
538    * Tests the org.exolab.castor.builder.javaclassmapping property for the 'element' value.
539    *
540    * @return True if the Source Generator is mapping schema elements to Java classes.
541    */
542   public boolean mappingSchemaElement2Java() {
543     String value = _localProps.getProperty(Property.JAVA_CLASS_MAPPING, "");
544     return ELEMENT_VALUE.equalsIgnoreCase(value);
545   } // -- mappingSchemaElement2Java
546 
547   /**
548    * Tests the org.exolab.castor.builder.javaclassmapping property for the 'type' value.
549    *
550    * @return True if the Source Generator is mapping schema types to Java classes.
551    */
552   public boolean mappingSchemaType2Java() {
553     String value = _localProps.getProperty(Property.JAVA_CLASS_MAPPING, "");
554     return TYPE_VALUE.equalsIgnoreCase(value);
555   } // -- mappingSchemaType2Java
556 
557   /**
558    * Overrides the current set of properties with the given properties. Once the properties are set,
559    * only a copy will be uses, so any changes to the given properties file after the fact will go
560    * unnoticed.
561    *
562    * @param properties the Properties file
563    */
564   public final void setDefaultProperties(final Properties properties) {
565     Properties defaults = null;
566     if (properties == null) {
567       defaults = _defaultProps;
568     } else {
569       defaults = new Properties(_defaultProps);
570       Enumeration<?> enumeration = properties.keys();
571       while (enumeration.hasMoreElements()) {
572         String name = (String) enumeration.nextElement();
573         defaults.setProperty(name, properties.getProperty(name));
574       }
575     }
576     _localProps = new Properties(defaults);
577     processNamespacePackageMappings(_localProps.getProperty(Property.NAMESPACE_PACKAGES_OLD, ""));
578     processNamespacePackageMappings(_localProps.getProperty(Property.NAMESPACE_PACKAGES, ""));
579   } // -- setDefaultProperties
580 
581   /**
582    * Sets the namespace to package mapping.
583    *
584    * @param ns the namespace URI to map
585    * @param packageName the package name
586    */
587   public final void setNamespacePackageMapping(final String ns, final String packageName) {
588     _nspackages.put(ns, packageName);
589   } // -- setNamespcaePackageMapping
590 
591   /**
592    * Sets the schemaLocation to package mapping.
593    *
594    * @param schemaLocation the schemaLocation to map
595    * @param packageName the package name to map to
596    */
597   public final void setLocationPackageMapping(final String schemaLocation,
598       final String packageName) {
599     _locpackages.put(schemaLocation, packageName);
600   }
601 
602   /**
603    * Returns a String representing the list of {@link JClassPrinterFactory} instances configured in
604    * the Castor XML code generator property file.
605    * 
606    * @return the list of {@link JClassPrinterFactory} instances as configured, as string.
607    */
608   public final String getJClassPrinterFactories() {
609     return _localProps.getProperty(Property.JCLASSPRINTER_FACTORIES);
610   }
611 
612 
613   /**
614    * Called by {@link #getDefault} to load the configuration the first time. Will not complain about
615    * inability to load configuration file from one of the default directories, but if it cannot find
616    * the JAR's configuration file, will throw a run time exception.
617    */
618   protected final synchronized void load() {
619     if (_defaultProps == null) {
620       // -- load defaults from JAR
621       _defaultProps = loadProperties(Property.RESOURCE_NAME, Property.CONFIG_FILENAME_PROPERTY);
622 
623       // -- load local defaults
624 
625       boolean found = false;
626 
627       // Get overriding configuration from the classpath, ignore if not found.
628       // If found, merge any existing properties.
629       try {
630         InputStream is =
631             SourceGenerator.class.getResourceAsStream("/" + Property.CONFIG_FILENAME_PROPERTY);
632         if (is != null) {
633           found = true;
634           _defaultProps.load(is);
635           is.close();
636         }
637       } catch (Exception except) {
638         // -- do nothing
639       }
640 
641       // -- if not found, either it doesn't exist, or "." is not part of the
642       // -- class path, try looking at local working directory
643       if (!found) {
644         try {
645           File file = new File(Property.CONFIG_FILENAME_PROPERTY);
646           if (file.exists() && file.canRead()) {
647             try (InputStream is = new FileInputStream(file)) {
648               _defaultProps.load(is);
649             }
650           }
651         } catch (Exception except) {
652           // -- do nothing
653         }
654       }
655     }
656 
657     AbstractProperties rtconf = XMLProperties.newInstance();
658 
659     // Parse XML namespace and package list from both castor.properties and
660     // castorbuilder.properties
661     processNamespacePackageMappings(rtconf.getString(Property.NAMESPACE_PACKAGES_OLD, ""));
662     processNamespacePackageMappings(rtconf.getString(Property.NAMESPACE_PACKAGES, ""));
663     processNamespacePackageMappings(_defaultProps.getProperty(Property.NAMESPACE_PACKAGES_OLD, ""));
664     processNamespacePackageMappings(_defaultProps.getProperty(Property.NAMESPACE_PACKAGES, ""));
665 
666     // -- backward compatibility with 0.9.3.9
667     String prop =
668         _defaultProps.getProperty(JavaNamingImpl.UPPER_CASE_AFTER_UNDERSCORE_PROPERTY, null);
669     if (prop != null) {
670       /*
671        * TODO: joachim: Argh! this shouldn't exist and it shouldn't be here! Setting a static
672        * attribute into a class because somewhere later the class is instantiated and use but then
673        * the attribute is required... I need to sort out the order how objects are created...
674        */
675       JavaNamingImpl._upperCaseAfterUnderscore = Boolean.valueOf(prop).booleanValue();
676     }
677   } // -- load
678 
679   /**
680    * Gets a Java package to an XML namespace URL.
681    * 
682    * @param nsURL the XML namespace URL to convert into a Java package name
683    * @return a Java package name
684    */
685   public final String lookupPackageByNamespace(final String nsURL) {
686     String namespaceURL = (nsURL == null) ? "" : nsURL;
687 
688     // Lookup Java package via NS
689     String javaPackage = _nspackages.get(namespaceURL);
690     if (javaPackage == null) {
691       return "";
692     }
693     return javaPackage;
694   } // -- lookupPackageNamespace
695 
696   /**
697    * Converts a schema location into a Java package.
698    *
699    * @param schemaLocation the Schema location to use to look up the Java package
700    * @return a Java package name
701    */
702   public final String lookupPackageByLocation(final String schemaLocation) {
703     if (schemaLocation == null) {
704       return "";
705     }
706 
707     // Lookup Java package via schemaLocation
708     // --Full path
709     String javaPackage = _locpackages.get(schemaLocation);
710     if (javaPackage == null) {
711       String cleanedSchemaLocation = schemaLocation;
712       // --maybe a relative schemaLocation was given
713       while (schemaLocation.startsWith(".")) {
714         if (schemaLocation.startsWith(SELF_DIRECTORY)) {
715           cleanedSchemaLocation = schemaLocation.substring(SELF_DIRECTORY_LENGTH);
716         } else if (schemaLocation.startsWith(PARENT_DIRECTORY)) {
717           cleanedSchemaLocation = schemaLocation.substring(PARENT_DIRECTORY_LENGTH);
718         }
719       }
720       Enumeration<String> keys = _locpackages.keys();
721       boolean found = false;
722       while (keys.hasMoreElements() && !found) {
723         String key = keys.nextElement();
724         if (cleanedSchemaLocation.endsWith(key)) {
725           javaPackage = _locpackages.get(key);
726           found = true;
727         }
728       }
729       if (javaPackage == null) {
730         javaPackage = "";
731       }
732     }
733 
734     return javaPackage;
735   } // -- lookupPackageLocation
736 
737   /**
738    * processes the given String which contains namespace-to-package mappings.
739    *
740    * @param mappings the namespace-to-package mappings
741    */
742   protected final void processNamespacePackageMappings(final String mappings) {
743     if (mappings == null) {
744       return;
745     }
746 
747     StringTokenizer tokens = new StringTokenizer(mappings, ",");
748     while (tokens.hasMoreTokens()) {
749       String token = tokens.nextToken();
750       int sepIdx = token.indexOf('=');
751       if (sepIdx < 0) {
752         continue;
753       }
754 
755       String ns = token.substring(0, sepIdx).trim();
756       String javaPackage = token.substring(sepIdx + 1).trim();
757       _nspackages.put(ns, javaPackage);
758     }
759   } // -- processNamespacePackageMappings
760 
761   /**
762    * indicates whether automatic class name conflict resolution during XML code generation should
763    * take place or not.
764    * 
765    * @return True if automatic mode should be used.
766    */
767   public boolean isAutomaticConflictResolution() {
768     String automaticConflictResolutionProperty =
769         _localProps.getProperty(Property.AUTOMATIC_CONFLICT_RESOLUTION, "false");
770     return "true".equalsIgnoreCase(automaticConflictResolutionProperty);
771   }
772 
773   /**
774    * Returns the type 'suffix' used for the type strategy during automatic class name conflict
775    * resolution during XML code generation; default to "" unless a value is specified.
776    * 
777    * @return The type suffix to be inserted between element name and type name
778    */
779   public String getAutomaticConflictResolutionTypeSuffix() {
780     return _localProps.getProperty(Property.AUTOMATIC_CONFLICT_RESOLUTION_TYPE_SUFFIX, "");
781   }
782 
783   /**
784    * To set the {@link JavaNaming} implementation to be used.
785    * 
786    * @param javaNaming JavaNaming implementation to be used
787    * @since 1.1.3
788    */
789   public void setJavaNaming(final JavaNaming javaNaming) {
790     _javaNaming = javaNaming;
791   }
792 
793   /**
794    * To get the {@link JavaNaming} implementation to be used.
795    * 
796    * @return JavaNaming implementation to be used
797    * @since 1.1.3
798    */
799   public JavaNaming getJavaNaming() {
800     return _javaNaming;
801   }
802 
803   /**
804    * adds a custom annotation builder.
805    * 
806    * @param annotationBuilder the builder
807    */
808   public void addAnnotationBuilder(final AnnotationBuilder annotationBuilder) {
809     this._annotationBuilders.add(annotationBuilder);
810   }
811 
812   /**
813    * returns all applied annotation builders.
814    * 
815    * @return a array of builders for type convenience
816    */
817   public AnnotationBuilder[] getAnnotationBuilders() {
818     return this._annotationBuilders.toArray(new AnnotationBuilder[this._annotationBuilders.size()]);
819   }
820 
821   /**
822    * Load the configuration will not complain about inability to load configuration file from one of
823    * the default directories, but if it cannot find the JAR's configuration file, will throw a run
824    * time exception.
825    * 
826    * @param resourceName Name of the source.
827    * @param fileName Name of the configuration file.
828    * @return A {@link Properties} instance holding the actual XML code generator configuration.
829    */
830   public static Properties loadProperties(final String resourceName, final String fileName) {
831     File file;
832     Properties properties = new Properties();
833 
834     boolean found = false;
835 
836     // Get detault configuration from the Castor JAR.
837     // Complain if not found.
838     InputStream resourceStream = null;
839     try {
840       resourceStream = AbstractProperties.class.getResourceAsStream(resourceName);
841       properties.load(resourceStream);
842 
843       // -- debug information:
844       // URL url = Configuration.class.getResource( resourceName );
845       // System.out.println("loading configuration: " + url.toExternalForm());
846       // -- end debug information
847 
848       found = true;
849     } catch (Exception except) {
850       // Do nothing as we will check classpath
851       // and java lib directory below
852     } finally {
853       if (resourceStream != null) {
854         try {
855           resourceStream.close();
856         } catch (IOException e) {
857           LOG.warn("Problem closing stream for " + resourceName);
858         }
859       }
860     }
861 
862     // Get overriding configuration from the Java
863     // library directory, ignore if not found. If
864     // found merge existing properties.
865     String javaHome = null;
866     try {
867       javaHome = System.getProperty("java.home");
868     } catch (SecurityException e) {
869       // Not a critical error, but users will need to know if they need to
870       // change their config.
871       LOG.warn(Messages.format("conf.privilegesError", e));
872     } catch (Exception e) {
873       // As we will be trying something else later, record the error for setup purposes,
874       // but do not actually treat as a critical failure.
875       LOG.warn(Messages.format("conf.nonCriticalError", e));
876     }
877 
878     if (javaHome != null) {
879       try {
880         file = new File(javaHome, "lib");
881         file = new File(file, fileName);
882         if (file.exists()) {
883           properties = new Properties(properties);
884           try (InputStream fileStream = new FileInputStream(file)) {
885             properties.load(fileStream);
886           }
887           found = true;
888         }
889       } catch (SecurityException e) {
890         // Not a critical error, but users will need to know if they need to change
891         // their config.
892         LOG.warn(Messages.format("conf.privilegesError", e));
893       } catch (IOException e) {
894         // Report that we were unable to load the resource.
895         LOG.warn(Messages.format("conf.nonCriticalError", e));
896       }
897     }
898 
899 
900     // -- Cannot find any castor.properties file(s).
901     if (!found) {
902       throw new RuntimeException(Messages.format("conf.noDefaultConfigurationFile", fileName));
903     }
904 
905     return properties;
906   }
907 
908   public boolean useOldFieldNaming() {
909     return "true".equalsIgnoreCase(_localProps.getProperty(Property.USE_OLD_FIELD_NAMING, FALSE));
910   }
911 }