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