1
2
3
4
5
6
7
8
9
10
11 package com.legstar.jaxb.plugin;
12
13 import java.io.IOException;
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.Locale;
18 import java.util.Map.Entry;
19
20 import javax.xml.bind.annotation.XmlSchemaType;
21
22 import org.apache.commons.lang.ArrayUtils;
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.w3c.dom.Element;
26 import org.xml.sax.ErrorHandler;
27
28 import com.legstar.coxb.CobolComplexType;
29 import com.legstar.coxb.CobolElement;
30 import com.legstar.coxb.CobolMarkup;
31 import com.legstar.coxb.CobolType;
32 import com.sun.codemodel.JAnnotationUse;
33 import com.sun.codemodel.JDefinedClass;
34 import com.sun.codemodel.JExpr;
35 import com.sun.codemodel.JFieldVar;
36 import com.sun.tools.xjc.BadCommandLineException;
37 import com.sun.tools.xjc.Options;
38 import com.sun.tools.xjc.Plugin;
39 import com.sun.tools.xjc.model.CClassInfo;
40 import com.sun.tools.xjc.model.CElement;
41 import com.sun.tools.xjc.model.CElementInfo;
42 import com.sun.tools.xjc.model.CPluginCustomization;
43 import com.sun.tools.xjc.model.CPropertyInfo;
44 import com.sun.tools.xjc.model.CReferencePropertyInfo;
45 import com.sun.tools.xjc.model.Model;
46 import com.sun.tools.xjc.model.nav.NClass;
47 import com.sun.tools.xjc.outline.ClassOutline;
48 import com.sun.tools.xjc.outline.FieldOutline;
49 import com.sun.tools.xjc.outline.Outline;
50 import com.sun.xml.bind.api.impl.NameConverter;
51
52
53
54
55
56
57
58
59 public class CobolJAXBAnnotator extends Plugin {
60
61
62
63
64
65 public static final String OPTION_NAME = "Xlegstar-code";
66
67
68 private boolean isEciCompatible;
69
70
71 private final Log _log = LogFactory.getLog(getClass());
72
73
74 public static final String OPTION_USAGE = " -Xlegstar-code : inject cobol binding annotation into the "
75 + "generated code";
76
77 private static final List < String > WINDOWS_RESERVED_FILE_NAMES = Arrays
78 .asList(new String[] { "CON", "PRN", "AUX", "NUL", "COM1", "COM2",
79 "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
80 "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7",
81 "LPT8", "LPT9" });
82
83 private static final String RESERVED_FILE_NAME_SUFFIX = "w";
84
85
86 @Override
87
88 public String getOptionName() {
89 return OPTION_NAME;
90 }
91
92
93 @Override
94
95 public String getUsage() {
96 return OPTION_USAGE;
97 }
98
99
100 @Override
101
102
103 public List < String > getCustomizationURIs() {
104 return Collections.singletonList(CobolMarkup.NS);
105 }
106
107
108 @Override
109
110
111
112 public boolean isCustomizationTagName(final String nsUri,
113 final String localName) {
114
115 return (nsUri.equals(CobolMarkup.NS) && (localName
116 .equals(CobolMarkup.ELEMENT)
117 || localName.equals(CobolMarkup.ELEMENT_VALUE) || localName
118 .equals(CobolMarkup.COMPLEX_TYPE)));
119 }
120
121
122
123
124
125 @Override
126 public int parseArgument(Options opt, String[] args, int i)
127 throws BadCommandLineException, IOException {
128 String arg = args[i];
129
130
131 if (arg.equals("-" + OPTION_NAME)) {
132 if (ArrayUtils.contains(args, "-eci")) {
133 opt.setNameConverter(new EciCompatibleNameConverter(), this);
134 } else {
135 opt.setNameConverter(new WinCompatibleNameConverter(), this);
136 }
137 }
138
139
140 if (arg.equals("-eci")) {
141 isEciCompatible = true;
142 return 1;
143 }
144 return 0;
145 }
146
147
148
149
150
151
152
153 public void postProcessModel(Model model, ErrorHandler errorHandler) {
154
155
156
157
158 if (isEciCompatible()) {
159 for (Entry < NClass, CClassInfo > entry : model.beans().entrySet()) {
160 CClassInfo classInfo = entry.getValue();
161 List < CPropertyInfo > properties = classInfo.getProperties();
162 for (CPropertyInfo property : properties) {
163 String publicName = property.getName(true);
164 String newPrivateName = Character.toLowerCase(publicName
165 .charAt(0)) + publicName.substring(1);
166 property.setName(false, newPrivateName);
167 }
168 }
169 }
170 }
171
172
173
174
175
176
177
178
179
180 @Override
181 public boolean run(final Outline model, final Options opt,
182 final ErrorHandler errorHandler) {
183
184 long start = System.currentTimeMillis();
185
186
187
188
189
190 for (CElementInfo eo : model.getModel().getAllElements()) {
191
192 if (_log.isDebugEnabled()) {
193 _log.debug("CobolJAXBAnnotator::run::CElementInfo::"
194 + eo.fullName());
195 }
196 CPluginCustomization c = eo.getCustomizations().find(
197 CobolMarkup.NS, CobolMarkup.ELEMENT);
198 if (c == null) {
199 continue;
200 }
201
202
203 c.markAsAcknowledged();
204
205 }
206
207
208
209
210
211 for (ClassOutline co : model.getClasses()) {
212
213 if (_log.isDebugEnabled()) {
214 _log.debug("CobolJAXBAnnotator::run::ClassOutline::"
215 + co.implClass);
216 }
217 annotateClass(co);
218
219 for (FieldOutline fo : co.getDeclaredFields()) {
220
221 if (_log.isDebugEnabled()) {
222 _log.debug("CobolJAXBAnnotator::run::FieldOutline::"
223 + fo.getPropertyInfo().getName(false));
224 }
225
226
227
228
229
230
231 CPluginCustomization c = null;
232 if (fo.getPropertyInfo() instanceof CReferencePropertyInfo) {
233 if (_log.isDebugEnabled()) {
234 _log.debug("FieldOutline is CReferencePropertyInfo");
235 }
236
237 for (CElement ce : ((CReferencePropertyInfo) fo
238 .getPropertyInfo()).getElements()) {
239 c = ce.getCustomizations().find(CobolMarkup.NS,
240 CobolMarkup.ELEMENT);
241 }
242 } else {
243 c = fo.getPropertyInfo().getCustomizations()
244 .find(CobolMarkup.NS, CobolMarkup.ELEMENT);
245 }
246
247 if (c == null) {
248 continue;
249 }
250 if (_log.isDebugEnabled()) {
251 String javaType = fo.getRawType().name();
252 _log.debug("CobolJAXBAnnotator::run::ClassOutline::"
253 + c.element.getLocalName() + " type=" + javaType);
254 }
255
256 c.markAsAcknowledged();
257
258
259 JDefinedClass coClass = co.implClass;
260 JFieldVar jf = coClass.fields().get(
261 fo.getPropertyInfo().getName(false));
262
263
264 JAnnotationUse ce = jf.annotate(CobolElement.class);
265 mapAnnotations(c, ce);
266
267 setDefaultValue(jf, c.element);
268
269
270
271
272
273 if (fo.getRawType().name().compareTo("byte[]") == 0) {
274 JAnnotationUse xmlSchemaType = jf
275 .annotate(XmlSchemaType.class);
276 xmlSchemaType.param("name", "hexBinary");
277 }
278
279 }
280 }
281
282 long end = System.currentTimeMillis();
283 if (_log.isDebugEnabled()) {
284 _log.debug("Cobol annotation success.");
285 _log.debug("Duration=" + (end - start) + " ms");
286 }
287
288 return true;
289 }
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306 protected void setDefaultValue(final JFieldVar jf, final Element e) {
307 if (!e.hasAttribute(CobolMarkup.VALUE)) {
308 return;
309 }
310 String value = e.getAttribute(CobolMarkup.VALUE).trim();
311 String type = jf.type().binaryName();
312 if (type.equals("java.lang.String")) {
313 jf.init(JExpr.lit(value));
314 } else {
315
316 if (value.length() == 0) {
317 return;
318 }
319
320 if (value.startsWith("+")) {
321 value = value.substring(1);
322 }
323 if (type.equals("java.math.BigDecimal")) {
324 jf.init(JExpr.direct("new BigDecimal(\"" + value + "\")"));
325 } else if (type.equals("java.math.BigInteger")) {
326 jf.init(JExpr.direct("new BigInteger(\"" + value + "\")"));
327 } else if (type.equals("short")) {
328 jf.init(JExpr.lit(Short.parseShort(value)));
329 } else if (type.equals("int")) {
330 jf.init(JExpr.lit(Integer.parseInt(value)));
331 } else if (type.equals("long")) {
332 jf.init(JExpr.lit(Long.parseLong(value)));
333 } else if (type.equals("float")) {
334 jf.init(JExpr.lit(Float.parseFloat(value)));
335 } else if (type.equals("double")) {
336 jf.init(JExpr.lit(Double.parseDouble(value)));
337 }
338 }
339
340 }
341
342
343
344
345
346
347 protected void annotateClass(final ClassOutline co) {
348 CPluginCustomization c = co.target.getCustomizations().find(
349 CobolMarkup.NS, CobolMarkup.COMPLEX_TYPE);
350 if (c == null) {
351 return;
352 }
353 c.markAsAcknowledged();
354
355 JAnnotationUse ce = co.implClass.annotate(CobolComplexType.class);
356 ce.param(CobolMarkup.JAVA_CLASS_NAME,
357 c.element.getAttribute(CobolMarkup.JAVA_CLASS_NAME));
358 }
359
360
361
362
363
364
365
366
367 protected void mapAnnotations(final CPluginCustomization c,
368 final JAnnotationUse ce) {
369
370 ce.param("cobolName", c.element.getAttribute(CobolMarkup.COBOL_NAME));
371
372 String cobolType = c.element.getAttribute(CobolMarkup.TYPE);
373
374 ce.param("type", CobolType.valueOf(cobolType));
375
376 setNumericParm(c.element, CobolMarkup.LEVEL_NUMBER, ce);
377 setBooleanParm(c.element, CobolMarkup.IS_JUSTIFIED_RIGHT, ce);
378 setBooleanParm(c.element, CobolMarkup.IS_SIGNED, ce);
379 setBooleanParm(c.element, CobolMarkup.IS_SIGN_LEADING, ce);
380 setBooleanParm(c.element, CobolMarkup.IS_SIGN_SEPARATE, ce);
381 setNumericParm(c.element, CobolMarkup.TOTAL_DIGITS, ce);
382 setNumericParm(c.element, CobolMarkup.FRACTION_DIGITS, ce);
383 setNumericParm(c.element, CobolMarkup.MIN_OCCURS, ce);
384 setNumericParm(c.element, CobolMarkup.MAX_OCCURS, ce);
385 setStringParm(c.element, CobolMarkup.DEPENDING_ON, ce);
386 setBooleanParm(c.element, CobolMarkup.IS_ODO_OBJECT, ce);
387 setStringParm(c.element, CobolMarkup.REDEFINES, ce);
388 setBooleanParm(c.element, CobolMarkup.IS_REDEFINED, ce);
389 setStringParm(c.element, CobolMarkup.PICTURE, ce);
390 setStringParm(c.element, CobolMarkup.USAGE, ce);
391 setStringParm(c.element, CobolMarkup.VALUE, ce);
392 setBooleanParm(c.element, CobolMarkup.IS_CUSTOM_VARIABLE, ce);
393 setStringParm(c.element, CobolMarkup.MARSHAL_CHOICE_STRATEGY, ce);
394 setStringParm(c.element, CobolMarkup.UNMARSHAL_CHOICE_STRATEGY, ce);
395 setNumericParm(c.element, CobolMarkup.SRCE_LINE, ce);
396
397 }
398
399
400
401
402
403
404
405
406 protected void setBooleanParm(final Element e, final String xmlMarkup,
407 final JAnnotationUse ce) {
408
409 String cobolProperty = xmlMarkup;
410
411
412
413
414 if (!cobolProperty.startsWith("is")) {
415 cobolProperty = "is"
416 + xmlMarkup.substring(0, 1)
417 .toUpperCase(Locale.getDefault())
418 + xmlMarkup.substring(1, xmlMarkup.length());
419 }
420 String value = e.getAttribute(xmlMarkup);
421 if (value == null || value.length() == 0) {
422 return;
423 }
424 ce.param(cobolProperty, Boolean.valueOf(value));
425 }
426
427
428
429
430
431
432
433
434 protected void setNumericParm(final Element e, final String xmlMarkup,
435 final JAnnotationUse ce) {
436
437 String cobolProperty = xmlMarkup;
438 String value = e.getAttribute(xmlMarkup);
439 if (value == null || value.length() == 0) {
440 return;
441 }
442 ce.param(cobolProperty, Integer.valueOf(value));
443 }
444
445
446
447
448
449
450
451
452 protected void setStringParm(final Element e, final String xmlMarkup,
453 final JAnnotationUse ce) {
454
455 String cobolProperty = xmlMarkup;
456 String value = e.getAttribute(xmlMarkup);
457 if (value == null || value.length() == 0) {
458 return;
459 }
460 ce.param(cobolProperty, value);
461 }
462
463
464
465
466 public boolean isEciCompatible() {
467 return isEciCompatible;
468 }
469
470
471
472
473
474
475
476
477
478
479
480
481 protected class EciCompatibleNameConverter extends
482 WinCompatibleNameConverter {
483
484
485 @Override
486 protected boolean isPunct(char c) {
487 return (c == '.' || c == '-' || c == ';'
488 || c == '\u00b7' || c == '\u0387' || c == '\u06dd' || c == '\u06de');
489 }
490
491
492 @Override
493 protected boolean isLetter(char c) {
494 return super.isLetter(c) || c == '_';
495 }
496
497
498 @Override
499 protected int classify(char c0) {
500 if (c0 == '_')
501 return OTHER_LETTER;
502 return super.classify(c0);
503 }
504
505
506 @Override
507 protected String toMixedCaseName(List < String > ss, boolean startUpper) {
508 StringBuilder sb = new StringBuilder();
509 if (!ss.isEmpty()) {
510 if (startUpper) {
511 sb.append(Character.toUpperCase(ss.get(0).charAt(0)));
512 sb.append(ss.get(0).substring(1));
513 } else {
514 sb.append(ss.get(0).toLowerCase());
515 }
516 for (int i = 1; i < ss.size(); i++)
517 sb.append(ss.get(i));
518 }
519 return sb.toString();
520 }
521
522
523 @Override
524 public String capitalize(String s) {
525 return s;
526 }
527 }
528
529
530
531
532
533
534 protected class WinCompatibleNameConverter extends NameConverter.Standard {
535 public String toClassName(String s) {
536 String className = super.toClassName(s);
537 return WINDOWS_RESERVED_FILE_NAMES
538 .contains(className.toUpperCase()) ? className
539 + RESERVED_FILE_NAME_SUFFIX : className;
540 }
541
542 }
543
544 }