View Javadoc
1   /*
2    * Copyright 2005 Werner Guttmann
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.exolab.castor.builder.factory;
15  
16  import java.util.Enumeration;
17  
18  import org.exolab.castor.builder.AnnotationBuilder;
19  import org.exolab.castor.builder.BuilderConfiguration;
20  import org.exolab.castor.builder.FactoryState;
21  import org.exolab.castor.builder.GroupNaming;
22  import org.exolab.castor.builder.SGTypes;
23  import org.exolab.castor.builder.SourceGenerator;
24  import org.exolab.castor.builder.TypeConversion;
25  import org.exolab.castor.builder.binding.ExtendedBinding;
26  import org.exolab.castor.builder.binding.XMLBindingComponent;
27  import org.exolab.castor.builder.binding.xml.EnumBindingType;
28  import org.exolab.castor.builder.binding.xml.EnumMember;
29  import org.exolab.castor.builder.types.XSString;
30  import org.exolab.castor.builder.types.XSType;
31  import org.exolab.castor.xml.schema.Facet;
32  import org.exolab.castor.xml.schema.SimpleType;
33  import org.exolab.javasource.JAnnotationType;
34  import org.exolab.javasource.JArrayType;
35  import org.exolab.javasource.JClass;
36  import org.exolab.javasource.JConstructor;
37  import org.exolab.javasource.JDocComment;
38  import org.exolab.javasource.JEnum;
39  import org.exolab.javasource.JEnumConstant;
40  import org.exolab.javasource.JField;
41  import org.exolab.javasource.JMethod;
42  import org.exolab.javasource.JModifiers;
43  import org.exolab.javasource.JParameter;
44  import org.exolab.javasource.JSourceCode;
45  import org.exolab.javasource.JType;
46  
47  /**
48   * This class creates the Java sources for XML Schema components that define an enumeration.
49   * 
50   * @author <a href="mailto:werner DOT guttmann AT gmx DOT net">Werner Guttmann</a>
51   * @version $Revision: 6287 $ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
52   */
53  public final class EnumerationFactory extends BaseFactory {
54  
55    /**
56     * The TypeConversion instance to use for mapping SimpleTypes into XSTypes.
57     */
58    private TypeConversion _typeConversion;
59  
60    /**
61     * A flag indicating that enumerated types should be constructed to perform case insensitive
62     * lookups based on the values.
63     */
64    private boolean _caseInsensitive = false;
65  
66    /**
67     * Current (hence maximum) suffix for init methods, used to avoid the static initializer limits of
68     * a JVM.
69     */
70    private int _maxSuffix = 0;
71  
72    /**
73     * Maximum number of enumeration-based constants within a class file.
74     */
75    private int _maxEnumerationsPerClass;
76  
77    /**
78     * Creates a new EnumerationFactory for the builder configuration given.
79     * 
80     * @param config the current BuilderConfiguration instance.
81     * @param groupNaming The group naming scheme to be used.
82     * @param sourceGenerator the calling source generator.
83     */
84    public EnumerationFactory(final BuilderConfiguration config, final GroupNaming groupNaming,
85        final SourceGenerator sourceGenerator) {
86      super(config, null, groupNaming, sourceGenerator);
87      _typeConversion = new TypeConversion(getConfig());
88  
89      // TODO[WG]: add code to read in max. value from builder property file
90      _maxEnumerationsPerClass = config.getMaximumNumberOfConstants();
91    } // -- SourceFactory
92  
93    /**
94     * Creates all the necessary enumeration code for a given SimpleType.
95     * 
96     * @param binding Extended binding instance
97     * @param simpleType the SimpleType we are processing an enumeration for
98     * @param state our current state
99     * @see #processEnumerationAsBaseType
100    */
101   void processEnumerationAsNewObject(final ExtendedBinding binding, final SimpleType simpleType,
102       final FactoryState state) {
103     // reset _maxSuffix value to 0
104     _maxSuffix = 0;
105     boolean generateConstantDefinitions = true;
106     int numberOfEnumerationFacets = simpleType.getNumberOfFacets(Facet.ENUMERATION);
107     if (numberOfEnumerationFacets > _maxEnumerationsPerClass) {
108       generateConstantDefinitions = false;
109     }
110 
111     Enumeration<Facet> enumeration = simpleType.getFacets(Facet.ENUMERATION);
112 
113     XMLBindingComponent component = new XMLBindingComponent(getConfig(), getGroupNaming());
114     if (binding != null) {
115       component.setBinding(binding);
116       component.setView(simpleType);
117     }
118 
119     // -- select naming for types and instances
120     boolean useValuesAsName = true;
121     useValuesAsName = selectNamingScheme(component, enumeration, useValuesAsName);
122 
123     enumeration = simpleType.getFacets(Facet.ENUMERATION);
124 
125     JClass jClass = state.getJClass();
126     String className = jClass.getLocalName();
127 
128     if (component.getJavaClassName() != null) {
129       className = component.getJavaClassName();
130     }
131 
132     // the java 5 way -> create an enum
133     if (state.getJClass() instanceof JEnum) {
134       createJava5Enum(simpleType, state, component, useValuesAsName, enumeration);
135       return;
136     }
137 
138     JField field = null;
139     JField fHash = new JField(SGTypes.createHashtable(getConfig().useJava50()), "_memberTable");
140     fHash.setInitString("init()");
141     fHash.getModifiers().setStatic(true);
142 
143     JSourceCode jsc = null;
144 
145     // -- modify constructor
146     JConstructor constructor = jClass.getConstructor(0);
147     constructor.getModifiers().makePrivate();
148     constructor.addParameter(new JParameter(JType.INT, "type"));
149     constructor.addParameter(new JParameter(SGTypes.STRING, "value"));
150     jsc = constructor.getSourceCode();
151     jsc.add("this.type = type;");
152     jsc.add("this.stringValue = value;");
153 
154     createValueOfMethod(jClass, className);
155     createEnumerateMethod(jClass, className);
156     createToStringMethod(jClass, className);
157     createInitMethod(jClass);
158     createReadResolveMethod(jClass);
159 
160     // -- Loop through "enumeration" facets
161     int count = 0;
162 
163     while (enumeration.hasMoreElements()) {
164       Facet facet = (Facet) enumeration.nextElement();
165 
166       String value = facet.getValue();
167 
168       String typeName = null;
169       String objName = null;
170 
171       if (useValuesAsName) {
172         objName = translateEnumValueToIdentifier(component.getEnumBinding(), facet);
173       } else {
174         objName = "VALUE_" + count;
175       }
176 
177       // -- create typeName
178       // -- Note: this could cause name conflicts
179       typeName = objName + "_TYPE";
180 
181       // -- Inheritence/Duplicate name cleanup
182       boolean addInitializerCode = true;
183       if (jClass.getField(objName) != null) {
184         // -- either inheritence, duplicate name, or error.
185         // -- if inheritence or duplicate name, always take
186         // -- the later definition. Do same if error, for now.
187         jClass.removeField(objName);
188         jClass.removeField(typeName);
189         addInitializerCode = false;
190       }
191 
192       if (generateConstantDefinitions) {
193         // -- handle int type
194         field = new JField(JType.INT, typeName);
195         field.setComment("The " + value + " type");
196         JModifiers modifiers = field.getModifiers();
197         modifiers.setFinal(true);
198         modifiers.setStatic(true);
199         modifiers.makePublic();
200         field.setInitString(Integer.toString(count));
201         jClass.addField(field);
202 
203         // -- handle Class type
204         field = new JField(jClass, objName);
205         field.setComment("The instance of the " + value + " type");
206 
207         modifiers = field.getModifiers();
208         modifiers.setFinal(true);
209         modifiers.setStatic(true);
210         modifiers.makePublic();
211 
212         StringBuilder init = new StringBuilder(32);
213         init.append("new ");
214         init.append(className);
215         init.append("(");
216         init.append(typeName);
217         init.append(", \"");
218         init.append(escapeValue(value));
219         init.append("\")");
220 
221         field.setInitString(init.toString());
222         jClass.addField(field);
223 
224       }
225       // -- initializer method
226 
227       if (addInitializerCode) {
228         jsc = getSourceCodeForInitMethod(jClass);
229         jsc.add("members.put(\"");
230         jsc.append(escapeValue(value));
231         if (_caseInsensitive) {
232           jsc.append("\".toLowerCase(), ");
233         } else {
234           jsc.append("\", ");
235         }
236         if (generateConstantDefinitions) {
237           jsc.append(objName);
238         } else {
239           String init = new StringBuilder(32).append("new ").append(className).append('(')
240               .append(count).append(", \"").append(escapeValue(value)).append("\")").toString();
241           jsc.append(init);
242         }
243         jsc.append(");");
244       }
245 
246       ++count;
247     }
248 
249     // -- finish init method
250     final JMethod method = jClass.getMethod(this.getInitMethodName(_maxSuffix), 0);
251     method.getSourceCode().add("return members;");
252 
253     // -- add memberTable to the class, we can only add this after all the
254     // types,
255     // -- or we'll create source code that will generate null pointer
256     // exceptions,
257     // -- because calling init() will try to add null values to the
258     // hashtable.
259     jClass.addField(fHash);
260 
261     // -- add internal type
262     field = new JField(JType.INT, "type");
263     field.getModifiers().setFinal(true);
264     jClass.addField(field);
265 
266     // -- add internal stringValue
267     field = new JField(SGTypes.STRING, "stringValue");
268     field.setInitString("null");
269     jClass.addField(field);
270 
271     createGetTypeMethod(jClass, className);
272   } // -- processEnumerationAsNewObject
273 
274   private void createJava5Enum(final SimpleType simpleType, final FactoryState state,
275       final XMLBindingComponent component, final boolean useValuesAsName,
276       final Enumeration<Facet> enumeration) {
277 
278     AnnotationBuilder[] annotationBuilders =
279         state.getSGStateInfo().getSourceGenerator().getAnnotationBuilders();
280 
281     JEnum jEnum = (JEnum) state.getJClass();
282 
283     jEnum.removeInterface("java.io.Serializable");
284     jEnum.removeAnnotation(new JAnnotationType("SuppressWarnings"));
285 
286     // add value field
287     JField valueField = new JField(new JClass("java.lang.String"), "value");
288     JModifiers modifiers = new JModifiers();
289     modifiers.setFinal(true);
290     modifiers.makePrivate();
291     valueField.setModifiers(modifiers);
292     jEnum.addField(valueField);
293 
294     // add enumConstants field
295     JField enumConstantsField =
296         new JField(new JClass("java.util.Map<java.lang.String, " + jEnum.getLocalName() + ">"),
297             "enumConstants");
298     modifiers = new JModifiers();
299     modifiers.setFinal(true);
300     modifiers.makePrivate();
301     modifiers.setStatic(true);
302     enumConstantsField.setModifiers(modifiers);
303     enumConstantsField
304         .setInitString("new java.util.HashMap<java.lang.String, " + jEnum.getLocalName() + ">()");
305     jEnum.addField(enumConstantsField);
306 
307     // add static initialization of enumConstants field
308     JSourceCode sourceCode = jEnum.getStaticInitializationCode();
309     sourceCode.add("for (" + jEnum.getLocalName() + " c: " + jEnum.getLocalName() + ".values()) {");
310     sourceCode.indent();
311     sourceCode.indent();
312     sourceCode.add(jEnum.getLocalName() + "." + enumConstantsField.getName() + ".put(c."
313         + valueField.getName() + ", c);");
314     sourceCode.unindent();
315     sourceCode.add("}");
316 
317     addValueMethod(jEnum);
318     addFromValueMethod(jEnum, enumConstantsField);
319     addSetValueMethod(jEnum);
320     addToStringMethod(jEnum);
321 
322     JConstructor constructor = jEnum.createConstructor();
323     constructor.addParameter(new JParameter(new JClass("java.lang.String"), "value"));
324     constructor.setSourceCode("this.value = value;");
325     modifiers = new JModifiers();
326     modifiers.makePrivate();
327     constructor.setModifiers(modifiers);
328     jEnum.addConstructor(constructor);
329 
330     int enumCount = 0;
331     while (enumeration.hasMoreElements()) {
332       Facet facet = enumeration.nextElement();
333       JEnumConstant enumConstant;
334       if (useValuesAsName) {
335         enumConstant =
336             new JEnumConstant(translateEnumValueToIdentifier(component.getEnumBinding(), facet),
337                 new String[] {"\"" + facet.getValue() + "\""});
338       } else {
339         enumConstant =
340             new JEnumConstant("VALUE_" + enumCount, new String[] {"\"" + facet.getValue() + "\""});
341       }
342 
343       // custom annotations
344       for (AnnotationBuilder annotationBuilder : annotationBuilders) {
345         annotationBuilder.addEnumConstantAnnotations(facet, enumConstant);
346       }
347 
348       jEnum.addEnumConstant(enumConstant);
349       enumCount++;
350     }
351 
352     // custom annotations
353     for (AnnotationBuilder annotationBuilder : annotationBuilders) {
354       annotationBuilder.addEnumAnnotations(simpleType, jEnum);
355     }
356   }
357 
358   /**
359    * Adds a {@link JMethod} instance for "value".
360    * 
361    * @param jEnum The {@link JEnum} instance to add this method to.
362    */
363   private void addValueMethod(JEnum jEnum) {
364     JMethod valueMethod =
365         new JMethod("value", new JClass("java.lang.String"), "the value of this constant");
366     valueMethod.setSourceCode("return this.value;");
367     jEnum.addMethod(valueMethod, false);
368   }
369 
370   /**
371    * Adds a {@link JMethod} instance for "toString".
372    * 
373    * @param jEnum The {@link JEnum} instance to add this method to.
374    */
375   private void addToStringMethod(JEnum jEnum) {
376     JMethod toStringMethod =
377         new JMethod("toString", new JClass("java.lang.String"), "the value of this constant");
378     toStringMethod.setSourceCode("return this.value;");
379     jEnum.addMethod(toStringMethod, false);
380   }
381 
382   /**
383    * Adds a {@link JMethod} instance for "setValue(String)".
384    * 
385    * @param jEnum The {@link JEnum} instance to add this method to.
386    */
387   private void addSetValueMethod(JEnum jEnum) {
388     JMethod setValueMethod = new JMethod("setValue");
389     setValueMethod.addParameter(new JParameter(new JClass("java.lang.String"), "value"));
390     jEnum.addMethod(setValueMethod, false);
391   }
392 
393   /**
394    * Adds a {@link JMethod} instance for "fromValue".
395    * 
396    * @param jEnum The {@link JEnum} instance to add this method to.
397    */
398   private void addFromValueMethod(JEnum jEnum, JField enumConstantsField) {
399     JModifiers modifiers;
400     JSourceCode sourceCode;
401     JMethod fromValueMethod = new JMethod("fromValue", jEnum, "the constant for this value");
402     fromValueMethod.addParameter(new JParameter(new JClass("java.lang.String"), "value"));
403     sourceCode = new JSourceCode();
404     sourceCode.add(jEnum.getLocalName() + " c = " + jEnum.getLocalName() + "."
405         + enumConstantsField.getName() + ".get(value);");
406     sourceCode.add("if (c != null) {");
407     sourceCode.indent();
408     sourceCode.add("return c;");
409     sourceCode.unindent();
410     sourceCode.add("}");
411     sourceCode.add("throw new IllegalArgumentException(value);");
412     fromValueMethod.setSourceCode(sourceCode);
413     modifiers = new JModifiers();
414     modifiers.setStatic(true);
415     fromValueMethod.setModifiers(modifiers);
416     jEnum.addMethod(fromValueMethod, false);
417   }
418 
419   /**
420    * Returns the JSourceCode instance for the current init() method, dealing with static initializer
421    * limits of the JVM by creating new init() methods as needed.
422    * 
423    * @param jClass The JClass instance for which an init method needs to be added
424    * @return the JSourceCode instance for the current init() method
425    */
426   private JSourceCode getSourceCodeForInitMethod(final JClass jClass) {
427     final JMethod currentInitMethod = jClass.getMethod(getInitMethodName(_maxSuffix), 0);
428     if (currentInitMethod.getSourceCode().size() > _maxEnumerationsPerClass) {
429       ++_maxSuffix;
430       JMethod mInit = createInitMethod(jClass);
431       currentInitMethod.getSourceCode().add("members.putAll(" + mInit.getName() + "());");
432       currentInitMethod.getSourceCode().add("return members;");
433 
434       return mInit.getSourceCode();
435     }
436     return currentInitMethod.getSourceCode();
437   }
438 
439   /**
440    * Returns the method name for an init method.
441    * 
442    * @param index index of the init method.
443    * @return the method name for an init method.
444    */
445   private String getInitMethodName(final int index) {
446     if (index == 0) {
447       return "init";
448     }
449 
450     return "init" + index;
451   }
452 
453   private boolean selectNamingScheme(final XMLBindingComponent component,
454       final Enumeration<Facet> enumeration, final boolean useValuesAsName) {
455     boolean duplicateTranslation = false;
456     short numberOfTranslationToSpecialCharacter = 0;
457 
458     while (enumeration.hasMoreElements()) {
459       Facet facet = enumeration.nextElement();
460       String possibleId = translateEnumValueToIdentifier(component.getEnumBinding(), facet);
461       if (possibleId.equals("_")) {
462         numberOfTranslationToSpecialCharacter++;
463         if (numberOfTranslationToSpecialCharacter > 1) {
464           duplicateTranslation = true;
465         }
466       }
467 
468       if (!getJavaNaming().isValidJavaIdentifier(possibleId)) {
469         return false;
470       }
471     }
472 
473     if (duplicateTranslation) {
474       return false;
475     }
476     return useValuesAsName;
477   }
478 
479   /**
480    * Creates 'getType()' method for this enumeration class.
481    * 
482    * @param jClass The enumeration class to create this method for.
483    * @param className The name of the class.
484    */
485   private void createGetTypeMethod(final JClass jClass, final String className) {
486     JMethod mGetType = new JMethod("getType", JType.INT, "the type of this " + className);
487     mGetType.getSourceCode().add("return this.type;");
488     JDocComment jdc = mGetType.getJDocComment();
489     jdc.appendComment("Returns the type of this " + className);
490     jClass.addMethod(mGetType);
491   }
492 
493   /**
494    * Creates 'readResolve(Object)' method for this enumeration class.
495    * 
496    * @param jClass The enumeration class to create this method for.
497    */
498   private void createReadResolveMethod(final JClass jClass) {
499     JDocComment jdc;
500     JSourceCode jsc;
501     JMethod mReadResolve = new JMethod("readResolve", SGTypes.OBJECT, "this deserialized object");
502     mReadResolve.getModifiers().makePrivate();
503     jClass.addMethod(mReadResolve);
504     jdc = mReadResolve.getJDocComment();
505     jdc.appendComment(" will be called during deserialization to replace ");
506     jdc.appendComment("the deserialized object with the correct constant ");
507     jdc.appendComment("instance.");
508     jsc = mReadResolve.getSourceCode();
509     jsc.add("return valueOf(this.stringValue);");
510   }
511 
512   /**
513    * Creates 'init()' method for this enumeration class.
514    * 
515    * @param jClass The enumeration class to create this method for.
516    * @return an 'init()' method for this enumeration class.
517    */
518   private JMethod createInitMethod(final JClass jClass) {
519     final String initMethodName = getInitMethodName(_maxSuffix);
520     JMethod mInit = new JMethod(initMethodName, SGTypes.createHashtable(getConfig().useJava50()),
521         "the initialized Hashtable for the member table");
522     jClass.addMethod(mInit);
523     mInit.getModifiers().makePrivate();
524     mInit.getModifiers().setStatic(true);
525     if (getConfig().useJava50()) {
526       mInit.getSourceCode().add("java.util.Hashtable<Object, Object> members"
527           + " = new java.util.Hashtable<Object, Object>();");
528     } else {
529       mInit.getSourceCode().add("java.util.Hashtable members = new java.util.Hashtable();");
530     }
531     return mInit;
532   }
533 
534   /**
535    * Creates 'toString()' method for this enumeration class.
536    * 
537    * @param jClass The enumeration class to create this method for.
538    * @param className The name of the class.
539    */
540   private void createToStringMethod(final JClass jClass, final String className) {
541     JMethod mToString =
542         new JMethod("toString", SGTypes.STRING, "the String representation of this " + className);
543     jClass.addMethod(mToString);
544     JDocComment jdc = mToString.getJDocComment();
545     jdc.appendComment("Returns the String representation of this ");
546     jdc.appendComment(className);
547     mToString.getSourceCode().add("return this.stringValue;");
548   }
549 
550   /**
551    * Creates 'enumerate()' method for this enumeration class.
552    * 
553    * @param jClass The enumeration class to create this method for.
554    * @param className The name of the class.
555    */
556   private void createEnumerateMethod(final JClass jClass, final String className) {
557     // TODO for the time being return Enumeration<Object> for Java 5.0;
558     // change
559     JMethod mEnumerate = new JMethod("enumerate",
560         SGTypes.createEnumeration(SGTypes.OBJECT, getConfig().useJava50(), true),
561         "an Enumeration over all possible instances of " + className);
562     mEnumerate.getModifiers().setStatic(true);
563     jClass.addMethod(mEnumerate);
564     JDocComment jdc = mEnumerate.getJDocComment();
565     jdc.appendComment("Returns an enumeration of all possible instances of ");
566     jdc.appendComment(className);
567     mEnumerate.getSourceCode().add("return _memberTable.elements();");
568   }
569 
570   /**
571    * Creates 'valueOf(String)' method for this enumeration class.
572    * 
573    * @param jClass The enumeration class to create this method for.
574    * @param className The name of the class.
575    */
576   private void createValueOfMethod(final JClass jClass, final String className) {
577     JMethod mValueOf =
578         new JMethod("valueOf", jClass, "the " + className + " value of parameter 'string'");
579     mValueOf.addParameter(new JParameter(SGTypes.STRING, "string"));
580     mValueOf.getModifiers().setStatic(true);
581     jClass.addMethod(mValueOf);
582 
583     JDocComment jdc = mValueOf.getJDocComment();
584     jdc.appendComment("Returns a new " + className);
585     jdc.appendComment(" based on the given String value.");
586 
587     JSourceCode jsc = mValueOf.getSourceCode();
588     jsc.add(
589         "java.lang.Object obj = null;\n" + "if (string != null) {\n"
590             + " obj = _memberTable.get(string{1});\n" + "}\n" + "if (obj == null) {\n"
591             + " String err = \"'\" + string + \"' is not a valid {0}\";\n"
592             + " throw new IllegalArgumentException(err);\n" + "}\n" + "return ({0}) obj;",
593         className, (_caseInsensitive ? ".toLowerCase()" : ""));
594 
595   }
596 
597   /**
598    * Creates all the necessary enumeration code from the given SimpleType. Enumerations are handled
599    * by creating an Object like the following:
600    * 
601    * <pre>
602    *     public class {name} {
603    *         // list of values
604    *         {type}[] values = {
605    *             ...
606    *         };
607    * 
608    *         // Returns true if the given value is part
609    *         // of this enumeration
610    *         public boolean contains({type} value);
611    * 
612    *         // Returns the {type} value whose String value
613    *         // is equal to the given String
614    *         public {type} valueOf(String strValue);
615    *     }
616    * </pre>
617    * 
618    * @param binding Extended binding instance
619    * @param simpleType the SimpleType we are processing an enumeration for
620    * @param state our current state
621    */
622   void processEnumerationAsBaseType(final ExtendedBinding binding, final SimpleType simpleType,
623       final FactoryState state) {
624     SimpleType base = (SimpleType) simpleType.getBaseType();
625     XSType baseType = null;
626 
627     if (base == null) {
628       baseType = new XSString();
629     } else {
630       baseType = _typeConversion.convertType(base, getConfig().useJava50());
631     }
632 
633     Enumeration<Facet> enumeration = simpleType.getFacets(Facet.ENUMERATION);
634 
635     JClass jClass = state.getJClass();
636     String className = jClass.getLocalName();
637 
638     JField fValues = null;
639     JDocComment jdc = null;
640     JSourceCode jsc = null;
641 
642     // -- modify constructor
643     JConstructor constructor = jClass.getConstructor(0);
644     constructor.getModifiers().makePrivate();
645 
646     fValues = new JField(new JArrayType(baseType.getJType(), getConfig().useJava50()), "values");
647 
648     // -- Loop through "enumeration" facets
649     // -- and create the default values for the type.
650     int count = 0;
651 
652     StringBuilder values = new StringBuilder("{\n");
653 
654     while (enumeration.hasMoreElements()) {
655       Facet facet = (Facet) enumeration.nextElement();
656       String value = facet.getValue();
657 
658       // -- Should we make sure the value is valid before proceeding??
659 
660       // -- we need to move this code to XSType so that we don't have to
661       // do
662       // -- special code here for each type
663 
664       if (count > 0) {
665         values.append(",\n");
666       }
667 
668       // -- indent for fun
669       values.append("    ");
670 
671       if (baseType.getType() == XSType.STRING_TYPE) {
672         values.append('\"');
673         // -- escape value
674         values.append(escapeValue(value));
675         values.append('\"');
676       } else {
677         values.append(value);
678       }
679 
680       ++count;
681     }
682 
683     values.append("\n}");
684 
685     fValues.setInitString(values.toString());
686     jClass.addField(fValues);
687 
688     // -- #valueOf method
689     JMethod method =
690         new JMethod("valueOf", jClass, "the String value of the provided " + baseType.getJType());
691     method.addParameter(new JParameter(SGTypes.STRING, "string"));
692     method.getModifiers().setStatic(true);
693     jClass.addMethod(method);
694     jdc = method.getJDocComment();
695     jdc.appendComment("Returns the " + baseType.getJType());
696     jdc.appendComment(" based on the given String value.");
697     jsc = method.getSourceCode();
698 
699     jsc.add("for (int i = 0; i < values.length; i++) {");
700     jsc.add("}");
701     jsc.add("throw new IllegalArgumentException(\"");
702     jsc.append("Invalid value for ");
703     jsc.append(className);
704     jsc.append(": \" + string + \".\");");
705   } // -- processEnumerationAsBaseType
706 
707   /**
708    * Attempts to translate a simpleType enumeration value into a legal java identifier. Translation
709    * is through a couple of simple rules:
710    * <ul>
711    * <li>if the value parses as a non-negative int, the string 'VALUE_' is prepended to it</li>
712    * <li>if the value parses as a negative int, the string 'VALUE_NEG_' is prepended to it</li>
713    * <li>the value is uppercased</li>
714    * <li>the characters <code>[](){}<>'`"</code> are removed</li>
715    * <li>the characters <code>|\/?~!@#$%^&*-+=:;.,</code> and any whitespace are replaced with
716    * <code>_</code></li>
717    * </ul>
718    * 
719    * @param enumBinding if not null, a possible custom binding for this enum
720    * @param facet the facet whose enum value is being translated.
721    * @return the identifier for the enum value
722    * 
723    * @author rhett-sutphin@uiowa.edu
724    */
725   private String translateEnumValueToIdentifier(final EnumBindingType enumBinding,
726       final Facet facet) {
727     String enumValue = facet.getValue();
728 
729     try {
730       String enumerationValue = null;
731       int intVal = Integer.parseInt(facet.getValue());
732 
733       String customMemberName = null;
734       if (enumBinding != null) {
735         customMemberName = getCustomMemberName(enumBinding, String.valueOf(intVal));
736       }
737 
738       if (customMemberName != null) {
739         enumerationValue = customMemberName;
740       } else {
741         if (intVal >= 0) {
742           enumerationValue = "VALUE_" + intVal;
743         } else {
744           enumerationValue = "VALUE_NEG_" + Math.abs(intVal);
745         }
746       }
747 
748       return enumerationValue;
749     } catch (NumberFormatException e) {
750       // just keep going
751     }
752 
753     StringBuilder sb = new StringBuilder(32);
754     String customMemberName = null;
755 
756     if (enumBinding != null) {
757       customMemberName = getCustomMemberName(enumBinding, enumValue);
758     }
759 
760     if (customMemberName != null) {
761       sb.append(customMemberName);
762     } else {
763       sb.append(enumValue.toUpperCase());
764       int i = 0;
765       while (i < sb.length()) {
766         char c = sb.charAt(i);
767         if ("[](){}<>'`\"".indexOf(c) >= 0) {
768           sb.deleteCharAt(i);
769           i--;
770         } else if (Character.isWhitespace(c) || "\\/?~!@#$%^&*-+=:;.,".indexOf(c) >= 0) {
771           sb.setCharAt(i, '_');
772         }
773         i++;
774       }
775     }
776     return sb.toString();
777   }
778 
779   /**
780    * Returns a custom member name (if defined).
781    * 
782    * @param enumBinding The {@link EnumBindingType} instance
783    * @param enumValue The enumeration value.
784    * @return the custom member name
785    */
786   private String getCustomMemberName(final EnumBindingType enumBinding, final String enumValue) {
787     // check whether there's a custom binding for the member name
788     String customMemberName = null;
789     EnumMember[] enumMembers = enumBinding.getEnumMember();
790     for (EnumMember enumMember : enumMembers) {
791       if (enumMember.getValue().equals(enumValue)) {
792         customMemberName = enumMember.getJavaName();
793       }
794     }
795     return customMemberName;
796   }
797 
798   /**
799    * Set to true if enumerated type lookups should be performed in a case insensitive manner.
800    * 
801    * @param caseInsensitive when true
802    */
803   public void setCaseInsensitive(final boolean caseInsensitive) {
804     _caseInsensitive = caseInsensitive;
805   }
806 
807   /**
808    * Escapes special characters in the given String so that it can be printed correctly.
809    * 
810    * @param str the String to escape
811    * @return the escaped String, or null if the given String was null.
812    */
813   private static String escapeValue(final String str) {
814     if (str == null) {
815       return str;
816     }
817 
818     StringBuilder sb = new StringBuilder();
819     char[] chars = str.toCharArray();
820 
821     for (char ch : chars) {
822       switch (ch) {
823         case '\\':
824         case '\"':
825         case '\'':
826           sb.append('\\');
827           break;
828         default:
829           break;
830       }
831       sb.append(ch);
832     }
833     return sb.toString();
834   } // -- escapeValue
835 
836 }