1
2
3
4
5
6
7
8
9
10
11
12
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
49
50
51
52
53 public final class EnumerationFactory extends BaseFactory {
54
55
56
57
58 private TypeConversion _typeConversion;
59
60
61
62
63
64 private boolean _caseInsensitive = false;
65
66
67
68
69
70 private int _maxSuffix = 0;
71
72
73
74
75 private int _maxEnumerationsPerClass;
76
77
78
79
80
81
82
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
90 _maxEnumerationsPerClass = config.getMaximumNumberOfConstants();
91 }
92
93
94
95
96
97
98
99
100
101 void processEnumerationAsNewObject(final ExtendedBinding binding, final SimpleType simpleType,
102 final FactoryState state) {
103
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
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
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
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
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
178
179 typeName = objName + "_TYPE";
180
181
182 boolean addInitializerCode = true;
183 if (jClass.getField(objName) != null) {
184
185
186
187 jClass.removeField(objName);
188 jClass.removeField(typeName);
189 addInitializerCode = false;
190 }
191
192 if (generateConstantDefinitions) {
193
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
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
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
250 final JMethod method = jClass.getMethod(this.getInitMethodName(_maxSuffix), 0);
251 method.getSourceCode().add("return members;");
252
253
254
255
256
257
258
259 jClass.addField(fHash);
260
261
262 field = new JField(JType.INT, "type");
263 field.getModifiers().setFinal(true);
264 jClass.addField(field);
265
266
267 field = new JField(SGTypes.STRING, "stringValue");
268 field.setInitString("null");
269 jClass.addField(field);
270
271 createGetTypeMethod(jClass, className);
272 }
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
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
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
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
344 for (AnnotationBuilder annotationBuilder : annotationBuilders) {
345 annotationBuilder.addEnumConstantAnnotations(facet, enumConstant);
346 }
347
348 jEnum.addEnumConstant(enumConstant);
349 enumCount++;
350 }
351
352
353 for (AnnotationBuilder annotationBuilder : annotationBuilders) {
354 annotationBuilder.addEnumAnnotations(simpleType, jEnum);
355 }
356 }
357
358
359
360
361
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
372
373
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
384
385
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
395
396
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
421
422
423
424
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
441
442
443
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
481
482
483
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
495
496
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
514
515
516
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
536
537
538
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
552
553
554
555
556 private void createEnumerateMethod(final JClass jClass, final String className) {
557
558
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
572
573
574
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
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
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
643 JConstructor constructor = jClass.getConstructor(0);
644 constructor.getModifiers().makePrivate();
645
646 fValues = new JField(new JArrayType(baseType.getJType(), getConfig().useJava50()), "values");
647
648
649
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
659
660
661
662
663
664 if (count > 0) {
665 values.append(",\n");
666 }
667
668
669 values.append(" ");
670
671 if (baseType.getType() == XSType.STRING_TYPE) {
672 values.append('\"');
673
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
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 }
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
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
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
781
782
783
784
785
786 private String getCustomMemberName(final EnumBindingType enumBinding, final String enumValue) {
787
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
800
801
802
803 public void setCaseInsensitive(final boolean caseInsensitive) {
804 _caseInsensitive = caseInsensitive;
805 }
806
807
808
809
810
811
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 }
835
836 }