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