1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
51
52
53
54
55
56
57
58 public final class EnumerationFactory extends BaseFactory {
59
60
61
62
63 private TypeConversion _typeConversion;
64
65
66
67
68
69 private boolean _caseInsensitive = false;
70
71
72
73
74
75 private int _maxSuffix = 0;
76
77
78
79
80 private int _maxEnumerationsPerClass;
81
82
83
84
85
86
87
88
89
90
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
98 _maxEnumerationsPerClass = config.getMaximumNumberOfConstants();
99 }
100
101
102
103
104
105
106
107
108
109
110
111
112 void processEnumerationAsNewObject(final ExtendedBinding binding,
113 final SimpleType simpleType, final FactoryState state) {
114
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
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
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
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
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
196
197 typeName = objName + "_TYPE";
198
199
200 boolean addInitializerCode = true;
201 if (jClass.getField(objName) != null) {
202
203
204
205 jClass.removeField(objName);
206 jClass.removeField(typeName);
207 addInitializerCode = false;
208 }
209
210 if (generateConstantDefinitions) {
211
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
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
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
274 final JMethod method = jClass.getMethod(this
275 .getInitMethodName(_maxSuffix), 0);
276 method.getSourceCode().add("return members;");
277
278
279
280
281
282
283
284 jClass.addField(fHash);
285
286
287 field = new JField(JType.INT, "type");
288 field.getModifiers().setFinal(true);
289 jClass.addField(field);
290
291
292 field = new JField(SGTypes.STRING, "stringValue");
293 field.setInitString("null");
294 jClass.addField(field);
295
296 createGetTypeMethod(jClass, className);
297 }
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
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
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
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
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
385 for (int i = 0; i < annotationBuilders.length; i++) {
386 AnnotationBuilder annotationBuilder = annotationBuilders[i];
387 annotationBuilder.addEnumAnnotations(simpleType, jEnum);
388 }
389 }
390
391
392
393
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
404
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
415
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
426
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
453
454
455
456
457
458
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
477
478
479
480
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
519
520
521
522
523
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
536
537
538
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
557
558
559
560
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
583
584
585
586
587
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
602
603
604
605
606
607
608 private void createEnumerateMethod(final JClass jClass,
609 final String className) {
610
611
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
627
628
629
630
631
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
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
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
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
713
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
723
724
725
726
727
728 if (count > 0) {
729 values.append(",\n");
730 }
731
732
733 values.append(" ");
734
735 if (baseType.getType() == XSType.STRING_TYPE) {
736 values.append('\"');
737
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
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 }
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
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
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
851
852
853
854
855
856
857
858 private String getCustomMemberName(final EnumBindingType enumBinding,
859 final String enumValue) {
860
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
873
874
875
876
877
878 public void setCaseInsensitive(final boolean caseInsensitive) {
879 _caseInsensitive = caseInsensitive;
880 }
881
882
883
884
885
886
887
888
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 }
913
914 }