1 /*
2 * Copyright (C) 2005 Christian Schulte <cs@schulte.it>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * o Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * o Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $JOMC: JomcTool.java 5307 2016-08-30 22:09:18Z schulte $
29 *
30 */
31 package org.jomc.tools;
32
33 import java.io.BufferedReader;
34 import java.io.ByteArrayInputStream;
35 import java.io.ByteArrayOutputStream;
36 import java.io.Closeable;
37 import java.io.FileNotFoundException;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.InputStreamReader;
41 import java.io.OutputStreamWriter;
42 import java.io.StringReader;
43 import java.lang.ref.Reference;
44 import java.lang.ref.SoftReference;
45 import java.lang.reflect.InvocationTargetException;
46 import java.net.URL;
47 import java.text.DateFormat;
48 import java.text.Format;
49 import java.text.MessageFormat;
50 import java.text.ParseException;
51 import java.text.SimpleDateFormat;
52 import java.util.ArrayList;
53 import java.util.Calendar;
54 import java.util.Collections;
55 import java.util.Enumeration;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.List;
59 import java.util.Locale;
60 import java.util.Map;
61 import java.util.ResourceBundle;
62 import java.util.Set;
63 import java.util.concurrent.ConcurrentHashMap;
64 import java.util.concurrent.CopyOnWriteArrayList;
65 import java.util.concurrent.ExecutorService;
66 import java.util.logging.Level;
67 import javax.activation.MimeTypeParseException;
68 import org.apache.commons.lang.StringEscapeUtils;
69 import org.apache.commons.lang.StringUtils;
70 import org.apache.velocity.Template;
71 import org.apache.velocity.VelocityContext;
72 import org.apache.velocity.app.VelocityEngine;
73 import org.apache.velocity.exception.ParseErrorException;
74 import org.apache.velocity.exception.ResourceNotFoundException;
75 import org.apache.velocity.exception.VelocityException;
76 import org.apache.velocity.runtime.RuntimeConstants;
77 import org.apache.velocity.runtime.RuntimeServices;
78 import org.apache.velocity.runtime.log.LogChute;
79 import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
80 import org.apache.velocity.runtime.resource.loader.URLResourceLoader;
81 import org.jomc.model.Argument;
82 import org.jomc.model.Dependency;
83 import org.jomc.model.Implementation;
84 import org.jomc.model.InheritanceModel;
85 import org.jomc.model.JavaIdentifier;
86 import org.jomc.model.JavaTypeName;
87 import org.jomc.model.Message;
88 import org.jomc.model.ModelObject;
89 import org.jomc.model.ModelObjectException;
90 import org.jomc.model.Modules;
91 import org.jomc.model.Multiplicity;
92 import org.jomc.model.Property;
93 import org.jomc.model.Specification;
94 import org.jomc.model.SpecificationReference;
95 import org.jomc.model.Text;
96 import org.jomc.model.Texts;
97 import org.jomc.model.modlet.ModelHelper;
98 import org.jomc.modlet.Model;
99
100 /**
101 * Base tool class.
102 *
103 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
104 * @version $JOMC: JomcTool.java 5307 2016-08-30 22:09:18Z schulte $
105 */
106 public class JomcTool
107 {
108
109 /**
110 * Listener interface.
111 */
112 public abstract static class Listener
113 {
114
115 /**
116 * Creates a new {@code Listener} instance.
117 */
118 public Listener()
119 {
120 super();
121 }
122
123 /**
124 * Gets called on logging.
125 *
126 * @param level The level of the event.
127 * @param message The message of the event or {@code null}.
128 * @param throwable The throwable of the event or {@code null}.
129 *
130 * @throws NullPointerException if {@code level} is {@code null}.
131 */
132 public void onLog( final Level level, final String message, final Throwable throwable )
133 {
134 if ( level == null )
135 {
136 throw new NullPointerException( "level" );
137 }
138 }
139
140 }
141
142 /**
143 * Empty byte array.
144 */
145 private static final byte[] NO_BYTES =
146 {
147 };
148
149 /**
150 * The prefix of the template location.
151 */
152 private static final String TEMPLATE_PREFIX =
153 JomcTool.class.getPackage().getName().replace( '.', '/' ) + "/templates/";
154
155 /**
156 * Constant for the default template profile.
157 */
158 private static final String DEFAULT_TEMPLATE_PROFILE = "jomc-java";
159
160 /**
161 * Constant for the name of the template profile property specifying a parent template profile name.
162 *
163 * @since 1.3
164 */
165 private static final String PARENT_TEMPLATE_PROFILE_PROPERTY_NAME = "parent-template-profile";
166
167 /**
168 * Constant for the name of the template profile property specifying the template encoding.
169 *
170 * @since 1.3
171 */
172 private static final String TEMPLATE_ENCODING_PROFILE_PROPERTY_NAME = "template-encoding";
173
174 /**
175 * The default encoding to use for reading templates.
176 *
177 * @since 1.3
178 */
179 private volatile String defaultTemplateEncoding;
180
181 /**
182 * The default template profile.
183 */
184 private static volatile String defaultTemplateProfile;
185
186 /**
187 * The log level events are logged at by default.
188 *
189 * @see #getDefaultLogLevel()
190 */
191 private static final Level DEFAULT_LOG_LEVEL = Level.WARNING;
192
193 /**
194 * The default log level.
195 */
196 private static volatile Level defaultLogLevel;
197
198 /**
199 * The model of the instance.
200 */
201 private volatile Model model;
202
203 /**
204 * The {@code VelocityEngine} of the instance.
205 */
206 private volatile VelocityEngine velocityEngine;
207
208 /**
209 * Flag indicating the default {@code VelocityEngine}.
210 *
211 * @since 1.2.4
212 */
213 private volatile boolean defaultVelocityEngine;
214
215 /**
216 * The location to search for templates in addition to searching the class path.
217 *
218 * @since 1.2
219 */
220 private volatile URL templateLocation;
221
222 /**
223 * The encoding to use for reading files.
224 */
225 private volatile String inputEncoding;
226
227 /**
228 * The encoding to use for writing files.
229 */
230 private volatile String outputEncoding;
231
232 /**
233 * The template parameters.
234 *
235 * @since 1.2
236 */
237 private final Map<String, Object> templateParameters =
238 Collections.synchronizedMap( new HashMap<String, Object>( 32 ) );
239 // ConcurrentHashMap does not allow for putting null values.
240
241 /**
242 * The template profile of the instance.
243 */
244 private volatile String templateProfile;
245
246 /**
247 * The indentation string of the instance.
248 */
249 private volatile String indentation;
250
251 /**
252 * The line separator of the instance.
253 */
254 private volatile String lineSeparator;
255
256 /**
257 * The listeners of the instance.
258 */
259 private final List<Listener> listeners = new CopyOnWriteArrayList<Listener>();
260
261 /**
262 * The log level of the instance.
263 */
264 private volatile Level logLevel;
265
266 /**
267 * The locale of the instance.
268 *
269 * @since 1.2
270 */
271 private volatile Locale locale;
272
273 /**
274 * The {@code ExecutorService} of the instance.
275 *
276 * @since 1.10
277 */
278 private volatile ExecutorService executorService;
279
280 /**
281 * Cached indentation strings.
282 */
283 private volatile Reference<Map<String, String>> indentationCache;
284
285 /**
286 * Cached templates.
287 *
288 * @since 1.3
289 */
290 private volatile Reference<Map<String, TemplateData>> templateCache;
291
292 /**
293 * Cached template profile context properties.
294 *
295 * @since 1.3
296 */
297 private volatile Reference<Map<String, java.util.Properties>> templateProfileContextPropertiesCache;
298
299 /**
300 * Cached template profile properties.
301 *
302 * @since 1.3
303 */
304 private volatile Reference<Map<String, java.util.Properties>> templateProfilePropertiesCache;
305
306 /**
307 * Cached Java keywords.
308 */
309 private volatile Reference<Set<String>> javaKeywordsCache;
310
311 /**
312 * Creates a new {@code JomcTool} instance.
313 */
314 public JomcTool()
315 {
316 super();
317 }
318
319 /**
320 * Creates a new {@code JomcTool} instance taking a {@code JomcTool} instance to initialize the new instance with.
321 *
322 * @param tool The instance to initialize the new instance with.
323 *
324 * @throws NullPointerException if {@code tool} is {@code null}.
325 * @throws IOException if copying {@code tool} fails.
326 */
327 public JomcTool( final JomcTool tool ) throws IOException
328 {
329 this();
330
331 if ( tool == null )
332 {
333 throw new NullPointerException( "tool" );
334 }
335
336 this.indentation = tool.indentation;
337 this.inputEncoding = tool.inputEncoding;
338 this.lineSeparator = tool.lineSeparator;
339 this.listeners.addAll( tool.listeners );
340 this.logLevel = tool.logLevel;
341 this.model = tool.model != null ? tool.model.clone() : null;
342 this.outputEncoding = tool.outputEncoding;
343 this.defaultTemplateEncoding = tool.defaultTemplateEncoding;
344 this.templateProfile = tool.templateProfile;
345 this.velocityEngine = tool.velocityEngine;
346 this.defaultVelocityEngine = tool.defaultVelocityEngine;
347 this.locale = tool.locale;
348 this.templateParameters.putAll( tool.templateParameters );
349 this.templateLocation =
350 tool.templateLocation != null ? new URL( tool.templateLocation.toExternalForm() ) : null;
351
352 this.executorService = tool.executorService;
353 }
354
355 /**
356 * Gets the list of registered listeners.
357 * <p>
358 * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
359 * to the returned list will be present inside the object. This is why there is no {@code set} method for the
360 * listeners property.
361 * </p>
362 *
363 * @return The list of registered listeners.
364 *
365 * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
366 */
367 public List<Listener> getListeners()
368 {
369 return this.listeners;
370 }
371
372 /**
373 * Gets the default log level events are logged at.
374 * <p>
375 * The default log level is controlled by system property {@code org.jomc.tools.JomcTool.defaultLogLevel} holding
376 * the log level to log events at by default. If that property is not set, the {@code WARNING} default is
377 * returned.
378 * </p>
379 *
380 * @return The log level events are logged at by default.
381 *
382 * @see #getLogLevel()
383 * @see Level#parse(java.lang.String)
384 */
385 public static Level getDefaultLogLevel()
386 {
387 if ( defaultLogLevel == null )
388 {
389 defaultLogLevel = Level.parse( System.getProperty( "org.jomc.tools.JomcTool.defaultLogLevel",
390 DEFAULT_LOG_LEVEL.getName() ) );
391
392 }
393
394 return defaultLogLevel;
395 }
396
397 /**
398 * Sets the default log level events are logged at.
399 *
400 * @param value The new default level events are logged at or {@code null}.
401 *
402 * @see #getDefaultLogLevel()
403 */
404 public static void setDefaultLogLevel( final Level value )
405 {
406 defaultLogLevel = value;
407 }
408
409 /**
410 * Gets the log level of the instance.
411 *
412 * @return The log level of the instance.
413 *
414 * @see #getDefaultLogLevel()
415 * @see #setLogLevel(java.util.logging.Level)
416 * @see #isLoggable(java.util.logging.Level)
417 */
418 public final Level getLogLevel()
419 {
420 if ( this.logLevel == null )
421 {
422 this.logLevel = getDefaultLogLevel();
423
424 if ( this.isLoggable( Level.CONFIG ) )
425 {
426 this.log( Level.CONFIG, getMessage( "defaultLogLevelInfo", this.logLevel.getLocalizedName() ), null );
427 }
428 }
429
430 return this.logLevel;
431 }
432
433 /**
434 * Sets the log level of the instance.
435 *
436 * @param value The new log level of the instance or {@code null}.
437 *
438 * @see #getLogLevel()
439 * @see #isLoggable(java.util.logging.Level)
440 */
441 public final void setLogLevel( final Level value )
442 {
443 this.logLevel = value;
444 }
445
446 /**
447 * Checks if a message at a given level is provided to the listeners of the instance.
448 *
449 * @param level The level to test.
450 *
451 * @return {@code true}, if messages at {@code level} are provided to the listeners of the instance;
452 * {@code false}, if messages at {@code level} are not provided to the listeners of the instance.
453 *
454 * @throws NullPointerException if {@code level} is {@code null}.
455 *
456 * @see #getLogLevel()
457 * @see #setLogLevel(java.util.logging.Level)
458 * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
459 */
460 public boolean isLoggable( final Level level )
461 {
462 if ( level == null )
463 {
464 throw new NullPointerException( "level" );
465 }
466
467 return level.intValue() >= this.getLogLevel().intValue();
468 }
469
470 /**
471 * Gets the Java package name of a specification.
472 *
473 * @param specification The specification to get the Java package name of.
474 *
475 * @return The Java package name of {@code specification} or {@code null}, if the specification does not reference a
476 * type.
477 *
478 * @throws NullPointerException if {@code specification} is {@code null}.
479 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
480 *
481 * @see Specification#getJavaTypeName()
482 * @see JavaTypeName#getPackageName()
483 *
484 * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
485 * removed in JOMC 2.0.
486 */
487 @Deprecated
488 public String getJavaPackageName( final Specification specification ) throws ModelObjectException
489 {
490 if ( specification == null )
491 {
492 throw new NullPointerException( "specification" );
493 }
494
495 final JavaTypeName javaTypeName = specification.getJavaTypeName();
496 return javaTypeName != null ? javaTypeName.getPackageName() : null;
497 }
498
499 /**
500 * Gets the Java type name of a specification.
501 *
502 * @param specification The specification to get the Java type name of.
503 * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
504 * {@code false}, to return the short type name (without package name prepended).
505 *
506 * @return The Java type name of the type referenced by the specification or {@code null}, if the specification does
507 * not reference a type.
508 *
509 * @throws NullPointerException if {@code specification} is {@code null}.
510 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
511 *
512 * @see Specification#getJavaTypeName()
513 * @see JavaTypeName#getName(boolean)
514 *
515 * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
516 * removed in JOMC 2.0.
517 */
518 @Deprecated
519 public String getJavaTypeName( final Specification specification, final boolean qualified )
520 throws ModelObjectException
521 {
522 if ( specification == null )
523 {
524 throw new NullPointerException( "specification" );
525 }
526
527 final JavaTypeName javaTypeName = specification.getJavaTypeName();
528 return javaTypeName != null ? javaTypeName.getName( qualified ) : null;
529 }
530
531 /**
532 * Gets the Java class path location of a specification.
533 *
534 * @param specification The specification to return the Java class path location of.
535 *
536 * @return The Java class path location of {@code specification} or {@code null}, if the specification does not
537 * reference a type.
538 *
539 * @throws NullPointerException if {@code specification} is {@code null}.
540 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
541 *
542 * @see Specification#getJavaTypeName()
543 * @see JavaTypeName#getQualifiedName()
544 *
545 * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
546 * removed in JOMC 2.0.
547 */
548 @Deprecated
549 public String getJavaClasspathLocation( final Specification specification ) throws ModelObjectException
550 {
551 if ( specification == null )
552 {
553 throw new NullPointerException( "specification" );
554 }
555
556 final JavaTypeName javaTypeName = specification.getJavaTypeName();
557 return javaTypeName != null ? javaTypeName.getQualifiedName().replace( '.', '/' ) : null;
558 }
559
560 /**
561 * Gets the Java package name of a specification reference.
562 *
563 * @param reference The specification reference to get the Java package name of.
564 *
565 * @return The Java package name of {@code reference} or {@code null}, if the referenced specification is not found
566 * or does not reference a type.
567 *
568 * @throws NullPointerException if {@code reference} is {@code null}.
569 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
570 *
571 * @see Modules#getSpecification(java.lang.String)
572 * @see Specification#getJavaTypeName()
573 * @see JavaTypeName#getPackageName()
574 *
575 * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
576 * removed in JOMC 2.0.
577 */
578 @Deprecated
579 public String getJavaPackageName( final SpecificationReference reference ) throws ModelObjectException
580 {
581 if ( reference == null )
582 {
583 throw new NullPointerException( "reference" );
584 }
585
586 Specification s = null;
587 String javaPackageName = null;
588
589 if ( this.getModules() != null
590 && ( s = this.getModules().getSpecification( reference.getIdentifier() ) ) != null )
591 {
592 final JavaTypeName javaTypeName = s.getJavaTypeName();
593 javaPackageName = javaTypeName != null ? javaTypeName.getPackageName() : null;
594 }
595 else if ( this.isLoggable( Level.WARNING ) )
596 {
597 this.log( Level.WARNING, getMessage( "specificationNotFound", reference.getIdentifier() ), null );
598 }
599
600 return javaPackageName;
601 }
602
603 /**
604 * Gets the name of a Java type of a given specification reference.
605 *
606 * @param reference The specification reference to get a Java type name of.
607 * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
608 * {@code false}, to return the short type name (without package name prepended).
609 *
610 * @return The Java type name of {@code reference} or {@code null}, if the referenced specification is not found
611 * or does not reference a type.
612 *
613 * @throws NullPointerException if {@code reference} is {@code null}.
614 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
615 *
616 * @see Modules#getSpecification(java.lang.String)
617 * @see Specification#getJavaTypeName()
618 * @see JavaTypeName#getName(boolean)
619 *
620 * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
621 * removed in JOMC 2.0.
622 */
623 @Deprecated
624 public String getJavaTypeName( final SpecificationReference reference, final boolean qualified )
625 throws ModelObjectException
626 {
627 if ( reference == null )
628 {
629 throw new NullPointerException( "reference" );
630 }
631
632 Specification s = null;
633 String typeName = null;
634
635 if ( this.getModules() != null
636 && ( s = this.getModules().getSpecification( reference.getIdentifier() ) ) != null )
637 {
638 final JavaTypeName javaTypeName = s.getJavaTypeName();
639 typeName = javaTypeName != null ? javaTypeName.getName( qualified ) : null;
640 }
641 else if ( this.isLoggable( Level.WARNING ) )
642 {
643 this.log( Level.WARNING, getMessage( "specificationNotFound", reference.getIdentifier() ), null );
644 }
645
646 return typeName;
647 }
648
649 /**
650 * Gets the Java package name of an implementation.
651 *
652 * @param implementation The implementation to get the Java package name of.
653 *
654 * @return The Java package name of {@code implementation} or {@code null}, if the implementation does not reference
655 * a type.
656 *
657 * @throws NullPointerException if {@code implementation} is {@code null}.
658 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
659 *
660 * @see Implementation#getJavaTypeName()
661 * @see JavaTypeName#getPackageName()
662 *
663 * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
664 * removed in JOMC 2.0.
665 */
666 @Deprecated
667 public String getJavaPackageName( final Implementation implementation ) throws ModelObjectException
668 {
669 if ( implementation == null )
670 {
671 throw new NullPointerException( "implementation" );
672 }
673
674 final JavaTypeName javaTypeName = implementation.getJavaTypeName();
675 return javaTypeName != null ? javaTypeName.getPackageName() : null;
676 }
677
678 /**
679 * Gets the Java type name of an implementation.
680 *
681 * @param implementation The implementation to get the Java type name of.
682 * @param qualified {@code true}, to return the fully qualified type name (with package name prepended);
683 * {@code false}, to return the short type name (without package name prepended).
684 *
685 * @return The Java type name of the type referenced by the implementation or {@code null}, if the implementation
686 * does not reference a type.
687 *
688 * @throws NullPointerException if {@code implementation} is {@code null}.
689 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
690 *
691 * @see Implementation#getJavaTypeName()
692 * @see JavaTypeName#getName(boolean)
693 *
694 * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
695 * removed in JOMC 2.0.
696 */
697 @Deprecated
698 public String getJavaTypeName( final Implementation implementation, final boolean qualified )
699 throws ModelObjectException
700 {
701 if ( implementation == null )
702 {
703 throw new NullPointerException( "implementation" );
704 }
705
706 final JavaTypeName javaTypeName = implementation.getJavaTypeName();
707 return javaTypeName != null ? javaTypeName.getName( qualified ) : null;
708 }
709
710 /**
711 * Gets the Java class path location of an implementation.
712 *
713 * @param implementation The implementation to return the Java class path location of.
714 *
715 * @return The Java class path location of {@code implementation} or {@code null}, if the implementation does not
716 * reference a type.
717 *
718 * @throws NullPointerException if {@code implementation} is {@code null}.
719 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
720 *
721 * @see Implementation#getJavaTypeName()
722 * @see JavaTypeName#getQualifiedName()
723 *
724 * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
725 * removed in JOMC 2.0.
726 */
727 @Deprecated
728 public String getJavaClasspathLocation( final Implementation implementation ) throws ModelObjectException
729 {
730 if ( implementation == null )
731 {
732 throw new NullPointerException( "implementation" );
733 }
734
735 final JavaTypeName javaTypeName = implementation.getJavaTypeName();
736 return javaTypeName != null ? javaTypeName.getQualifiedName().replace( '.', '/' ) : null;
737 }
738
739 /**
740 * Gets a list of names of all Java types an implementation implements.
741 *
742 * @param implementation The implementation to get names of all implemented Java types of.
743 * @param qualified {@code true}, to return the fully qualified type names (with package name prepended);
744 * {@code false}, to return the short type names (without package name prepended).
745 *
746 * @return An unmodifiable list of names of all Java types implemented by {@code implementation}.
747 *
748 * @throws NullPointerException if {@code implementation} is {@code null}.
749 * @throws ModelObjectException if compiling the name of a referenced type to a {@code JavaTypeName} fails.
750 *
751 * @deprecated As of JOMC 1.2, replaced by method {@link #getImplementedJavaTypeNames(org.jomc.model.Implementation, boolean)}.
752 * This method will be removed in version 2.0.
753 */
754 @Deprecated
755 public List<String> getJavaInterfaceNames( final Implementation implementation, final boolean qualified )
756 throws ModelObjectException
757 {
758 if ( implementation == null )
759 {
760 throw new NullPointerException( "implementation" );
761 }
762
763 return this.getImplementedJavaTypeNames( implementation, qualified );
764 }
765
766 /**
767 * Gets a list of names of all Java types an implementation implements.
768 *
769 * @param implementation The implementation to get names of all implemented Java types of.
770 * @param qualified {@code true}, to return the fully qualified type names (with package name prepended);
771 * {@code false}, to return the short type names (without package name prepended).
772 *
773 * @return An unmodifiable list of names of all Java types implemented by {@code implementation}.
774 *
775 * @throws NullPointerException if {@code implementation} is {@code null}.
776 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
777 *
778 * @since 1.2
779 *
780 * @deprecated As of JOMC 1.4, please use method {@link Modules#getImplementedJavaTypeNames(java.lang.String)}.
781 * This method will be removed in JOMC 2.0.
782 */
783 @Deprecated
784 public List<String> getImplementedJavaTypeNames( final Implementation implementation, final boolean qualified )
785 throws ModelObjectException
786 {
787 if ( implementation == null )
788 {
789 throw new NullPointerException( "implementation" );
790 }
791
792 List<String> col = null;
793
794 if ( this.getModules() != null )
795 {
796 final List<JavaTypeName> javaTypeNames =
797 this.getModules().getImplementedJavaTypeNames( implementation.getIdentifier() );
798
799 if ( javaTypeNames != null )
800 {
801 col = new ArrayList<String>( javaTypeNames.size() );
802
803 for ( int i = 0, s0 = javaTypeNames.size(); i < s0; i++ )
804 {
805 if ( !col.contains( javaTypeNames.get( i ).getName( qualified ) ) )
806 {
807 col.add( javaTypeNames.get( i ).getName( qualified ) );
808 }
809 }
810 }
811 }
812 else if ( this.isLoggable( Level.WARNING ) )
813 {
814 this.log( Level.WARNING, getMessage( "modulesNotFound", this.getModel().getIdentifier() ), null );
815 }
816
817 return Collections.unmodifiableList( col != null ? col : Collections.<String>emptyList() );
818 }
819
820 /**
821 * Gets the Java type name of an argument.
822 *
823 * @param argument The argument to get the Java type name of.
824 *
825 * @return The Java type name of the type referenced by the argument or {@code null}, if the argument does not
826 * reference a type.
827 *
828 * @throws NullPointerException if {@code argument} is {@code null}.
829 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
830 *
831 * @see Argument#getJavaTypeName()
832 * @see JavaTypeName#getName(boolean)
833 *
834 * @deprecated As of JOMC 1.4, please use method {@link Argument#getJavaTypeName()}. This method will be removed in
835 * JOMC 2.0.
836 */
837 @Deprecated
838 public String getJavaTypeName( final Argument argument ) throws ModelObjectException
839 {
840 if ( argument == null )
841 {
842 throw new NullPointerException( "argument" );
843 }
844
845 final JavaTypeName javaTypeName = argument.getJavaTypeName();
846 return javaTypeName != null ? javaTypeName.getName( true ) : null;
847 }
848
849 /**
850 * Gets a Java method parameter name of an argument.
851 *
852 * @param argument The argument to get the Java method parameter name of.
853 *
854 * @return The Java method parameter name of {@code argument}.
855 *
856 * @throws NullPointerException if {@code argument} is {@code null}.
857 * @throws ModelObjectException if compiling the name of the argument to a {@code JavaIdentifier} fails.
858 *
859 * @see Argument#getJavaVariableName()
860 *
861 * @since 1.2
862 *
863 * @deprecated As of JOMC 1.4, please use method {@link Argument#getJavaVariableName()}. This method will be
864 * removed in JOMC 2.0.
865 */
866 @Deprecated
867 public String getJavaMethodParameterName( final Argument argument ) throws ModelObjectException
868 {
869 if ( argument == null )
870 {
871 throw new NullPointerException( "argument" );
872 }
873
874 return this.getJavaMethodParameterName( argument.getName() );
875 }
876
877 /**
878 * Gets the Java type name of a property.
879 *
880 * @param property The property to get the Java type name of.
881 * @param boxify {@code true}, to return the name of the Java wrapper class when the type is a Java primitive type;
882 * {@code false}, to return the exact binary name (unboxed name) of the Java type.
883 *
884 * @return The Java type name of the type referenced by the property or {@code null}, if the property does not
885 * reference a type.
886 *
887 * @throws NullPointerException if {@code property} is {@code null}.
888 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
889 *
890 * @see Property#getJavaTypeName()
891 * @see JavaTypeName#getBoxedName()
892 * @see JavaTypeName#getName(boolean)
893 *
894 * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaTypeName()}. This method will be removed in
895 * JOMC 2.0.
896 */
897 @Deprecated
898 public String getJavaTypeName( final Property property, final boolean boxify ) throws ModelObjectException
899 {
900 if ( property == null )
901 {
902 throw new NullPointerException( "property" );
903 }
904
905 JavaTypeName javaTypeName = property.getJavaTypeName();
906
907 if ( javaTypeName != null )
908 {
909 if ( boxify && javaTypeName.isPrimitive() )
910 {
911 javaTypeName = javaTypeName.getBoxedName();
912 }
913
914 return javaTypeName.getName( true );
915 }
916
917 return null;
918 }
919
920 /**
921 * Gets a flag indicating the type of a given property is a Java primitive.
922 *
923 * @param property The property to query.
924 *
925 * @return {@code true}, if the Java type referenced by the property is primitive or {@code false}, if the property
926 * does not reference a type or if the Java type referenced by the property is not primitive.
927 *
928 * @throws NullPointerException if {@code property} is {@code null}.
929 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
930 *
931 * @see Property#getJavaTypeName()
932 * @see JavaTypeName#isPrimitive()
933 *
934 * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaTypeName()}. This method will be removed in
935 * JOMC 2.0.
936 */
937 @Deprecated
938 public boolean isJavaPrimitiveType( final Property property ) throws ModelObjectException
939 {
940 if ( property == null )
941 {
942 throw new NullPointerException( "property" );
943 }
944
945 final JavaTypeName javaTypeName = property.getJavaTypeName();
946 return javaTypeName != null && javaTypeName.isPrimitive();
947 }
948
949 /**
950 * Gets the name of a Java getter method of a given property.
951 *
952 * @param property The property to get a Java getter method name of.
953 *
954 * @return The Java getter method name of {@code property}.
955 *
956 * @throws NullPointerException if {@code property} is {@code null}.
957 * @throws ModelObjectException if compiling the name of the property to a {@code JavaIdentifier} fails.
958 *
959 * @see Property#getJavaGetterMethodName()
960 *
961 * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaGetterMethodName()}. This method will be
962 * removed in JOMC 2.0.
963 */
964 @Deprecated
965 public String getJavaGetterMethodName( final Property property ) throws ModelObjectException
966 {
967 if ( property == null )
968 {
969 throw new NullPointerException( "property" );
970 }
971
972 String prefix = "get";
973
974 final String javaTypeName = this.getJavaTypeName( property, true );
975 if ( Boolean.class.getName().equals( javaTypeName ) )
976 {
977 prefix = "is";
978 }
979
980 return prefix + this.getJavaIdentifier( property.getName(), true );
981 }
982
983 /**
984 * Gets the name of a Java setter method of a given property.
985 *
986 * @param property The property to get a Java setter method name of.
987 *
988 * @return The Java setter method name of {@code property}.
989 *
990 * @throws NullPointerException if {@code property} is {@code null}.
991 * @throws ModelObjectException if compiling the name of the property to a {@code JavaIdentifier} fails.
992 *
993 * @see Property#getJavaSetterMethodName()
994 *
995 * @since 1.2
996 *
997 * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaSetterMethodName()}. This method will be
998 * removed in JOMC 2.0.
999 */
1000 @Deprecated
1001 public String getJavaSetterMethodName( final Property property ) throws ModelObjectException
1002 {
1003 if ( property == null )
1004 {
1005 throw new NullPointerException( "property" );
1006 }
1007
1008 return "set" + this.getJavaIdentifier( property.getName(), true );
1009 }
1010
1011 /**
1012 * Gets a Java method parameter name of a property.
1013 *
1014 * @param property The property to get the Java method parameter name of.
1015 *
1016 * @return The Java method parameter name of {@code property}.
1017 *
1018 * @throws NullPointerException if {@code property} is {@code null}.
1019 * @throws ModelObjectException if copmiling the name of the property to a {@code JavaIdentifier} fails.
1020 *
1021 * @see Property#getJavaVariableName()
1022 *
1023 * @since 1.2
1024 *
1025 * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaVariableName()}. This method will be
1026 * removed in JOMC 2.0.
1027 */
1028 @Deprecated
1029 public String getJavaMethodParameterName( final Property property ) throws ModelObjectException
1030 {
1031 if ( property == null )
1032 {
1033 throw new NullPointerException( "property" );
1034 }
1035
1036 return this.getJavaMethodParameterName( property.getName() );
1037 }
1038
1039 /**
1040 * Gets a Java field name of a property.
1041 *
1042 * @param property The property to get the Java field name of.
1043 *
1044 * @return The Java field name of {@code property}.
1045 *
1046 * @throws NullPointerException if {@code property} is {@code null}.
1047 * @throws ModelObjectException if compiling the name of the property to a {@code JavaIdentifier} fails.
1048 *
1049 * @see Property#getJavaVariableName()
1050 *
1051 * @since 1.3
1052 *
1053 * @deprecated As of JOMC 1.4, please use method {@link Property#getJavaVariableName()}. This method will be removed
1054 * in JOMC 2.0.
1055 */
1056 @Deprecated
1057 public String getJavaFieldName( final Property property ) throws ModelObjectException
1058 {
1059 if ( property == null )
1060 {
1061 throw new NullPointerException( "property" );
1062 }
1063
1064 return this.getJavaFieldName( property.getName() );
1065 }
1066
1067 /**
1068 * Gets the name of a Java type of a given dependency.
1069 *
1070 * @param dependency The dependency to get a dependency Java type name of.
1071 *
1072 * @return The Java type name of the dependency or {@code null}, if the referenced specification is not found or
1073 * does not reference a type.
1074 *
1075 * @throws NullPointerException if {@code dependency} is {@code null}.
1076 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
1077 *
1078 * @deprecated As of JOMC 1.4, please use method {@link Modules#getDependencyJavaTypeName(java.lang.String, java.lang.String)}.
1079 * This method will be removed in JOMC 2.0.
1080 */
1081 @Deprecated
1082 public String getJavaTypeName( final Dependency dependency ) throws ModelObjectException
1083 {
1084 if ( dependency == null )
1085 {
1086 throw new NullPointerException( "dependency" );
1087 }
1088
1089 Specification s = null;
1090 StringBuilder typeName = null;
1091 String javaTypeName = null;
1092
1093 try
1094 {
1095 if ( this.getModules() != null
1096 && ( s = this.getModules().getSpecification( dependency.getIdentifier() ) ) != null )
1097 {
1098 if ( s.getClazz() != null )
1099 {
1100 typeName = new StringBuilder( s.getClazz().length() );
1101 typeName.append( this.getJavaTypeName( s, true ) );
1102
1103 if ( s.getMultiplicity() == Multiplicity.MANY && dependency.getImplementationName() == null )
1104 {
1105 typeName.append( "[]" );
1106 }
1107
1108 javaTypeName = JavaTypeName.parse( typeName.toString() ).getName( true );
1109 }
1110 }
1111 else if ( this.isLoggable( Level.WARNING ) )
1112 {
1113 this.log( Level.WARNING, getMessage( "specificationNotFound", dependency.getIdentifier() ), null );
1114 }
1115
1116 return javaTypeName;
1117 }
1118 catch ( final ParseException e )
1119 {
1120 throw new ModelObjectException( getMessage( "dependencyJavaTypeNameParseException", typeName,
1121 getMessage( e ) ), e );
1122
1123 }
1124 }
1125
1126 /**
1127 * Gets the name of a Java getter method of a given dependency.
1128 *
1129 * @param dependency The dependency to get a Java getter method name of.
1130 *
1131 * @return The Java getter method name of {@code dependency}.
1132 *
1133 * @throws NullPointerException if {@code dependency} is {@code null}.
1134 * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
1135 *
1136 * @see Dependency#getJavaGetterMethodName()
1137 *
1138 * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaGetterMethodName()}. This method will be
1139 * removed in JOMC 2.0.
1140 */
1141 @Deprecated
1142 public String getJavaGetterMethodName( final Dependency dependency ) throws ModelObjectException
1143 {
1144 if ( dependency == null )
1145 {
1146 throw new NullPointerException( "dependency" );
1147 }
1148
1149 return "get" + this.getJavaIdentifier( dependency.getName(), true );
1150 }
1151
1152 /**
1153 * Gets the name of a Java setter method of a given dependency.
1154 *
1155 * @param dependency The dependency to get a Java setter method name of.
1156 *
1157 * @return The Java setter method name of {@code dependency}.
1158 *
1159 * @throws NullPointerException if {@code dependency} is {@code null}.
1160 * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
1161 *
1162 * @see Dependency#getJavaSetterMethodName()
1163 *
1164 * @since 1.2
1165 *
1166 * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaSetterMethodName()}. This method will be
1167 * removed in JOMC 2.0.
1168 */
1169 @Deprecated
1170 public String getJavaSetterMethodName( final Dependency dependency ) throws ModelObjectException
1171 {
1172 if ( dependency == null )
1173 {
1174 throw new NullPointerException( "dependency" );
1175 }
1176
1177 return "set" + this.getJavaIdentifier( dependency.getName(), true );
1178 }
1179
1180 /**
1181 * Gets a Java method parameter name of a dependency.
1182 *
1183 * @param dependency The dependency to get the Java method parameter name of.
1184 *
1185 * @return The Java method parameter name of {@code dependency}.
1186 *
1187 * @throws NullPointerException if {@code dependency} is {@code null}.
1188 * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
1189 *
1190 * @see Dependency#getJavaVariableName()
1191 *
1192 * @since 1.2
1193 *
1194 * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaVariableName()}. This method will be
1195 * removed in JOMC 2.0.
1196 */
1197 @Deprecated
1198 public String getJavaMethodParameterName( final Dependency dependency ) throws ModelObjectException
1199 {
1200 if ( dependency == null )
1201 {
1202 throw new NullPointerException( "dependency" );
1203 }
1204
1205 return this.getJavaMethodParameterName( dependency.getName() );
1206 }
1207
1208 /**
1209 * Gets a Java field name of a dependency.
1210 *
1211 * @param dependency The dependency to get the Java field name of.
1212 *
1213 * @return The Java field name of {@code dependency}.
1214 *
1215 * @throws NullPointerException if {@code dependency} is {@code null}.
1216 * @throws ModelObjectException if compiling the name of the dependency to a {@code JavaIdentifier} fails.
1217 *
1218 * @see Dependency#getJavaVariableName()
1219 *
1220 * @since 1.3
1221 *
1222 * @deprecated As of JOMC 1.4, please use method {@link Dependency#getJavaVariableName()}. This method will be
1223 * removed in JOMC 2.0.
1224 */
1225 @Deprecated
1226 public String getJavaFieldName( final Dependency dependency ) throws ModelObjectException
1227 {
1228 if ( dependency == null )
1229 {
1230 throw new NullPointerException( "dependency" );
1231 }
1232
1233 return this.getJavaFieldName( dependency.getName() );
1234 }
1235
1236 /**
1237 * Gets the name of a Java getter method of a given message.
1238 *
1239 * @param message The message to get a Java getter method name of.
1240 *
1241 * @return The Java getter method name of {@code message}.
1242 *
1243 * @throws NullPointerException if {@code message} is {@code null}.
1244 * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
1245 *
1246 * @see Message#getJavaGetterMethodName()
1247 *
1248 * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaGetterMethodName()}. This method will be
1249 * removed in JOMC 2.0.
1250 */
1251 @Deprecated
1252 public String getJavaGetterMethodName( final Message message ) throws ModelObjectException
1253 {
1254 if ( message == null )
1255 {
1256 throw new NullPointerException( "message" );
1257 }
1258
1259 return "get" + this.getJavaIdentifier( message.getName(), true );
1260 }
1261
1262 /**
1263 * Gets the name of a Java setter method of a given message.
1264 *
1265 * @param message The message to get a Java setter method name of.
1266 *
1267 * @return The Java setter method name of {@code message}.
1268 *
1269 * @throws NullPointerException if {@code message} is {@code null}.
1270 * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
1271 *
1272 * @see Message#getJavaSetterMethodName()
1273 *
1274 * @since 1.2
1275 *
1276 * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaSetterMethodName()}. This method will be
1277 * removed in JOMC 2.0.
1278 */
1279 @Deprecated
1280 public String getJavaSetterMethodName( final Message message ) throws ModelObjectException
1281 {
1282 if ( message == null )
1283 {
1284 throw new NullPointerException( "message" );
1285 }
1286
1287 return "set" + this.getJavaIdentifier( message.getName(), true );
1288 }
1289
1290 /**
1291 * Gets a Java method parameter name of a message.
1292 *
1293 * @param message The message to get the Java method parameter name of.
1294 *
1295 * @return The Java method parameter name of {@code message}.
1296 *
1297 * @throws NullPointerException if {@code message} is {@code null}.
1298 * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
1299 *
1300 * @see Message#getJavaVariableName()
1301 *
1302 * @since 1.2
1303 *
1304 * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaVariableName()}. This method will be removed
1305 * in JOMC 2.0.
1306 */
1307 @Deprecated
1308 public String getJavaMethodParameterName( final Message message ) throws ModelObjectException
1309 {
1310 if ( message == null )
1311 {
1312 throw new NullPointerException( "message" );
1313 }
1314
1315 return this.getJavaMethodParameterName( message.getName() );
1316 }
1317
1318 /**
1319 * Gets a Java field name of a message.
1320 *
1321 * @param message The message to get the Java field name of.
1322 *
1323 * @return The Java field name of {@code message}.
1324 *
1325 * @throws NullPointerException if {@code message} is {@code null}.
1326 * @throws ModelObjectException if compiling the name of the message to a {@code JavaIdentifier} fails.
1327 *
1328 * @see Message#getJavaVariableName()
1329 *
1330 * @since 1.3
1331 *
1332 * @deprecated As of JOMC 1.4, please use method {@link Message#getJavaVariableName()}. This method will be removed
1333 * in JOMC 2.0.
1334 */
1335 @Deprecated
1336 public String getJavaFieldName( final Message message ) throws ModelObjectException
1337 {
1338 if ( message == null )
1339 {
1340 throw new NullPointerException( "message" );
1341 }
1342
1343 return this.getJavaFieldName( message.getName() );
1344 }
1345
1346 /**
1347 * Gets the Java modifier name of a dependency of a given implementation.
1348 *
1349 * @param implementation The implementation declaring the dependency to get a Java modifier name of.
1350 * @param dependency The dependency to get a Java modifier name of.
1351 *
1352 * @return The Java modifier name of {@code dependency} of {@code implementation}.
1353 *
1354 * @throws NullPointerException if {@code implementation} or {@code dependency} is {@code null}.
1355 *
1356 * @deprecated As of JOMC 1.4, please use method {@link Modules#getDependencyJavaModifierName(java.lang.String, java.lang.String)}.
1357 * This method will be removed in JOMC 2.0.
1358 */
1359 @Deprecated
1360 public String getJavaModifierName( final Implementation implementation, final Dependency dependency )
1361 {
1362 if ( implementation == null )
1363 {
1364 throw new NullPointerException( "implementation" );
1365 }
1366 if ( dependency == null )
1367 {
1368 throw new NullPointerException( "dependency" );
1369 }
1370
1371 String modifierName = "private";
1372
1373 if ( this.getModules() != null )
1374 {
1375 modifierName =
1376 this.getModules().getDependencyJavaModifierName( implementation.getIdentifier(), dependency.getName() );
1377
1378 if ( modifierName == null )
1379 {
1380 modifierName = "private";
1381 }
1382 }
1383
1384 return modifierName;
1385 }
1386
1387 /**
1388 * Gets the Java modifier name of a message of a given implementation.
1389 *
1390 * @param implementation The implementation declaring the message to get a Java modifier name of.
1391 * @param message The message to get a Java modifier name of.
1392 *
1393 * @return The Java modifier name of {@code message} of {@code implementation}.
1394 *
1395 * @throws NullPointerException if {@code implementation} or {@code message} is {@code null}.
1396 *
1397 * @deprecated As of JOMC 1.4, please use method {@link Modules#getMessageJavaModifierName(java.lang.String, java.lang.String)}.
1398 * This method will be removed in JOMC 2.0.
1399 */
1400 @Deprecated
1401 public String getJavaModifierName( final Implementation implementation, final Message message )
1402 {
1403 if ( implementation == null )
1404 {
1405 throw new NullPointerException( "implementation" );
1406 }
1407 if ( message == null )
1408 {
1409 throw new NullPointerException( "message" );
1410 }
1411
1412 String modifierName = "private";
1413
1414 if ( this.getModules() != null )
1415 {
1416 modifierName =
1417 this.getModules().getMessageJavaModifierName( implementation.getIdentifier(), message.getName() );
1418
1419 if ( modifierName == null )
1420 {
1421 modifierName = "private";
1422 }
1423 }
1424
1425 return modifierName;
1426 }
1427
1428 /**
1429 * Gets the Java modifier name of a property of a given implementation.
1430 *
1431 * @param implementation The implementation declaring the property to get a Java modifier name of.
1432 * @param property The property to get a Java modifier name of.
1433 *
1434 * @return The Java modifier name of {@code property} of {@code implementation}.
1435 *
1436 * @throws NullPointerException if {@code implementation} or {@code property} is {@code null}.
1437 *
1438 * @deprecated As of JOMC 1.4, please use method {@link Modules#getPropertyJavaModifierName(java.lang.String, java.lang.String)}.
1439 * This method will be removed in JOMC 2.0.
1440 */
1441 @Deprecated
1442 public String getJavaModifierName( final Implementation implementation, final Property property )
1443 {
1444 if ( implementation == null )
1445 {
1446 throw new NullPointerException( "implementation" );
1447 }
1448 if ( property == null )
1449 {
1450 throw new NullPointerException( "property" );
1451 }
1452
1453 String modifierName = "private";
1454
1455 if ( this.getModules() != null )
1456 {
1457 modifierName =
1458 this.getModules().getPropertyJavaModifierName( implementation.getIdentifier(), property.getName() );
1459
1460 if ( modifierName == null )
1461 {
1462 modifierName = "private";
1463 }
1464 }
1465
1466 return modifierName;
1467 }
1468
1469 /**
1470 * Formats a text to a Javadoc comment.
1471 *
1472 * @param text The text to format to a Javadoc comment.
1473 * @param indentationLevel The indentation level of the comment.
1474 * @param linePrefix The text to prepend lines with.
1475 *
1476 * @return {@code text} formatted to a Javadoc comment.
1477 *
1478 * @throws NullPointerException if {@code text} or {@code linePrefix} is {@code null}.
1479 * @throws IllegalArgumentException if {@code indentationLevel} is negative.
1480 * @throws ModelObjectException if compiling the type of the text to a {@code MimeType} fails.
1481 *
1482 * @deprecated As of JOMC 1.4, please use method {@link Text#getJavadocComment(java.lang.String, java.lang.String)}.
1483 * This method will be removed in JOMC 2.0.
1484 */
1485 @Deprecated
1486 public String getJavadocComment( final Text text, final int indentationLevel, final String linePrefix )
1487 throws ModelObjectException
1488 {
1489 if ( text == null )
1490 {
1491 throw new NullPointerException( "text" );
1492 }
1493 if ( linePrefix == null )
1494 {
1495 throw new NullPointerException( "linePrefix" );
1496 }
1497 if ( indentationLevel < 0 )
1498 {
1499 throw new IllegalArgumentException( Integer.toString( indentationLevel ) );
1500 }
1501
1502 BufferedReader reader = null;
1503 boolean suppressExceptionOnClose = true;
1504
1505 try
1506 {
1507 String javadoc = "";
1508
1509 if ( text.getValue() != null )
1510 {
1511 final String indent = this.getIndentation( indentationLevel );
1512 reader = new BufferedReader( new StringReader( text.getValue() ) );
1513 final StringBuilder builder = new StringBuilder( text.getValue().length() );
1514
1515 String line;
1516 while ( ( line = reader.readLine() ) != null )
1517 {
1518 builder.append( this.getLineSeparator() ).append( indent ).append( linePrefix ).
1519 append( line.replaceAll( "\\/\\*\\*", "/*" ).replaceAll( "\\*/", "/" ) );
1520
1521 }
1522
1523 if ( builder.length() > 0 )
1524 {
1525 javadoc =
1526 builder.substring( this.getLineSeparator().length() + indent.length() + linePrefix.length() );
1527
1528 if ( !text.getMimeType().match( "text/html" ) )
1529 {
1530 javadoc = StringEscapeUtils.escapeHtml( javadoc );
1531 }
1532 }
1533 }
1534
1535 suppressExceptionOnClose = false;
1536 return javadoc;
1537 }
1538 catch ( final MimeTypeParseException e )
1539 {
1540 throw new AssertionError( e );
1541 }
1542 catch ( final IOException e )
1543 {
1544 throw new AssertionError( e );
1545 }
1546 finally
1547 {
1548 try
1549 {
1550 if ( reader != null )
1551 {
1552 reader.close();
1553 }
1554 }
1555 catch ( final IOException e )
1556 {
1557 if ( suppressExceptionOnClose )
1558 {
1559 this.log( Level.SEVERE, getMessage( e ), e );
1560 }
1561 else
1562 {
1563 throw new AssertionError( e );
1564 }
1565 }
1566 }
1567 }
1568
1569 /**
1570 * Formats a text from a list of texts to a Javadoc comment.
1571 *
1572 * @param texts The list of texts to format to a Javadoc comment.
1573 * @param indentationLevel The indentation level of the comment.
1574 * @param linePrefix The text to prepend lines with.
1575 *
1576 * @return The text corresponding to the locale of the instance from the list of texts formatted to a Javadoc
1577 * comment.
1578 *
1579 * @throws NullPointerException if {@code texts} or {@code linePrefix} is {@code null}.
1580 * @throws IllegalArgumentException if {@code indentationLevel} is negative.
1581 * @throws ModelObjectException if compiling a referenced type to a {@code MimeType} fails.
1582 *
1583 * @see #getLocale()
1584 *
1585 * @since 1.2
1586 *
1587 * @deprecated As of JOMC 1.4, please use method {@link Text#getJavadocComment(java.lang.String, java.lang.String)}.
1588 * This method will be removed in JOMC 2.0.
1589 */
1590 @Deprecated
1591 public String getJavadocComment( final Texts texts, final int indentationLevel, final String linePrefix )
1592 throws ModelObjectException
1593 {
1594 if ( texts == null )
1595 {
1596 throw new NullPointerException( "texts" );
1597 }
1598 if ( linePrefix == null )
1599 {
1600 throw new NullPointerException( "linePrefix" );
1601 }
1602 if ( indentationLevel < 0 )
1603 {
1604 throw new IllegalArgumentException( Integer.toString( indentationLevel ) );
1605 }
1606
1607 return this.getJavadocComment( texts.getText( this.getLocale().getLanguage() ), indentationLevel, linePrefix );
1608 }
1609
1610 /**
1611 * Formats a string to a Java string with unicode escapes.
1612 *
1613 * @param str The string to format to a Java string or {@code null}.
1614 *
1615 * @return {@code str} formatted to a Java string or {@code null}.
1616 *
1617 * @see StringEscapeUtils#escapeJava(java.lang.String)
1618 */
1619 public String getJavaString( final String str )
1620 {
1621 return StringEscapeUtils.escapeJava( str );
1622 }
1623
1624 /**
1625 * Formats a string to a Java class path location.
1626 *
1627 * @param str The string to format or {@code null}.
1628 * @param absolute {@code true} to return an absolute class path location; {@code false} to return a relative
1629 * class path location.
1630 *
1631 * @return {@code str} formatted to a Java class path location.
1632 *
1633 * @since 1.3
1634 *
1635 * @deprecated As of JOMC 1.4, please use {@link JavaTypeName#getQualifiedName()}. This method will be removed in
1636 * JOMC 2.0.
1637 */
1638 @Deprecated
1639 public String getJavaClasspathLocation( final String str, final boolean absolute )
1640 {
1641 String classpathLocation = null;
1642
1643 if ( str != null )
1644 {
1645 classpathLocation = str.replace( '.', '/' );
1646
1647 if ( absolute )
1648 {
1649 classpathLocation = "/" + classpathLocation;
1650 }
1651 }
1652
1653 return classpathLocation;
1654 }
1655
1656 /**
1657 * Formats a string to a Java identifier.
1658 *
1659 * @param str The string to format or {@code null}.
1660 * @param capitalize {@code true}, to return an identifier with the first character upper cased; {@code false}, to
1661 * return an identifier with the first character lower cased.
1662 *
1663 * @return {@code str} formatted to a Java identifier or {@code null}.
1664 *
1665 * @since 1.2
1666 *
1667 * @deprecated As of JOMC 1.4, please use method {@link #toJavaVariableName(java.lang.String)}. This method will be
1668 * removed in JOMC 2.0.
1669 */
1670 @Deprecated
1671 public String getJavaIdentifier( final String str, final boolean capitalize )
1672 {
1673 String identifier = null;
1674
1675 if ( str != null )
1676 {
1677 final int len = str.length();
1678 final StringBuilder builder = new StringBuilder( len );
1679 boolean uc = capitalize;
1680
1681 for ( int i = 0; i < len; i++ )
1682 {
1683 final char c = str.charAt( i );
1684 final String charString = Character.toString( c );
1685
1686 if ( builder.length() > 0 )
1687 {
1688 if ( Character.isJavaIdentifierPart( c ) )
1689 {
1690 builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
1691 uc = false;
1692 }
1693 else
1694 {
1695 uc = true;
1696 }
1697 }
1698 else if ( Character.isJavaIdentifierStart( c ) )
1699 {
1700 builder.append( uc
1701 ? charString.toUpperCase( this.getLocale() )
1702 : charString.toLowerCase( this.getLocale() ) );
1703
1704 uc = false;
1705 }
1706 else
1707 {
1708 uc = capitalize;
1709 }
1710 }
1711
1712 identifier = builder.toString();
1713
1714 if ( identifier.length() <= 0 && this.isLoggable( Level.WARNING ) )
1715 {
1716 this.log( Level.WARNING, getMessage( "invalidJavaIdentifier", str ), null );
1717 }
1718 }
1719
1720 return identifier;
1721 }
1722
1723 /**
1724 * Formats a string to a Java method parameter name.
1725 *
1726 * @param str The string to format or {@code null}.
1727 *
1728 * @return {@code str} formatted to a Java method parameter name or {@code null}.
1729 *
1730 * @since 1.3
1731 *
1732 * @deprecated As of JOMC 1.4, please use method {@link #toJavaVariableName(java.lang.String)}. This method will be
1733 * removed in JOMC 2.0.
1734 */
1735 @Deprecated
1736 public String getJavaMethodParameterName( final String str )
1737 {
1738 String methodParameterName = null;
1739
1740 if ( str != null )
1741 {
1742 final int len = str.length();
1743 final StringBuilder builder = new StringBuilder( len );
1744 boolean uc = false;
1745
1746 for ( int i = 0; i < len; i++ )
1747 {
1748 final char c = str.charAt( i );
1749 final String charString = Character.toString( c );
1750
1751 if ( builder.length() > 0 )
1752 {
1753 if ( Character.isJavaIdentifierPart( c ) )
1754 {
1755 builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
1756 uc = false;
1757 }
1758 else
1759 {
1760 uc = true;
1761 }
1762 }
1763 else if ( Character.isJavaIdentifierStart( c ) )
1764 {
1765 builder.append( charString.toLowerCase( this.getLocale() ) );
1766 }
1767 }
1768
1769 methodParameterName = builder.toString();
1770
1771 if ( methodParameterName.length() <= 0 && this.isLoggable( Level.WARNING ) )
1772 {
1773 this.log( Level.WARNING, getMessage( "invalidJavaMethodParameterName", str ), null );
1774 }
1775
1776 if ( this.getJavaKeywords().contains( methodParameterName ) )
1777 {
1778 methodParameterName = "_" + methodParameterName;
1779 }
1780 }
1781
1782 return methodParameterName;
1783 }
1784
1785 /**
1786 * Formats a string to a Java field name.
1787 *
1788 * @param str The string to format or {@code null}.
1789 *
1790 * @return {@code str} formatted to a Java field name or {@code null}.
1791 *
1792 * @since 1.3
1793 *
1794 * @deprecated As of JOMC 1.4, please use method {@link #toJavaVariableName(java.lang.String)}. This method will be
1795 * removed in JOMC 2.0.
1796 */
1797 @Deprecated
1798 public String getJavaFieldName( final String str )
1799 {
1800 String fieldName = null;
1801
1802 if ( str != null )
1803 {
1804 final int len = str.length();
1805 final StringBuilder builder = new StringBuilder( len );
1806 boolean uc = false;
1807
1808 for ( int i = 0; i < len; i++ )
1809 {
1810 final char c = str.charAt( i );
1811 final String charString = Character.toString( c );
1812
1813 if ( builder.length() > 0 )
1814 {
1815 if ( Character.isJavaIdentifierPart( c ) )
1816 {
1817 builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString );
1818 uc = false;
1819 }
1820 else
1821 {
1822 uc = true;
1823 }
1824 }
1825 else if ( Character.isJavaIdentifierStart( c ) )
1826 {
1827 builder.append( charString.toLowerCase( this.getLocale() ) );
1828 }
1829 }
1830
1831 fieldName = builder.toString();
1832
1833 if ( fieldName.length() <= 0 && this.isLoggable( Level.WARNING ) )
1834 {
1835 this.log( Level.WARNING, getMessage( "invalidJavaFieldName", str ), null );
1836 }
1837
1838 if ( this.getJavaKeywords().contains( fieldName ) )
1839 {
1840 fieldName = "_" + fieldName;
1841 }
1842 }
1843
1844 return fieldName;
1845 }
1846
1847 /**
1848 * Formats a string to a Java constant name.
1849 *
1850 * @param str The string to format or {@code null}.
1851 *
1852 * @return {@code str} formatted to a Java constant name or {@code null}.
1853 *
1854 * @since 1.3
1855 *
1856 * @deprecated As of JOMC 1.4, please use method {@link #toJavaConstantName(java.lang.String)}. This method will be
1857 * removed in JOMC 2.0.
1858 */
1859 @Deprecated
1860 public String getJavaConstantName( final String str )
1861 {
1862 String name = null;
1863
1864 if ( str != null )
1865 {
1866 final int len = str.length();
1867 final StringBuilder builder = new StringBuilder( len );
1868 boolean separator = false;
1869
1870 for ( int i = 0; i < len; i++ )
1871 {
1872 final char c = str.charAt( i );
1873
1874 if ( builder.length() > 0 ? Character.isJavaIdentifierPart( c ) : Character.isJavaIdentifierStart( c ) )
1875 {
1876 if ( builder.length() > 0 )
1877 {
1878 if ( !separator )
1879 {
1880 final char previous = builder.charAt( builder.length() - 1 );
1881 separator = Character.isLowerCase( previous ) && Character.isUpperCase( c );
1882 }
1883
1884 if ( separator )
1885 {
1886 builder.append( '_' );
1887 }
1888 }
1889
1890 builder.append( c );
1891 separator = false;
1892 }
1893 else
1894 {
1895 separator = true;
1896 }
1897 }
1898
1899 name = builder.toString().toUpperCase( this.getLocale() );
1900
1901 if ( name.length() <= 0 && this.isLoggable( Level.WARNING ) )
1902 {
1903 this.log( Level.WARNING, getMessage( "invalidJavaConstantName", str ), null );
1904 }
1905 }
1906
1907 return name;
1908 }
1909
1910 /**
1911 * Compiles a string to a Java constant name.
1912 *
1913 * @param str The string to compile or {@code null}.
1914 *
1915 * @return {@code str} compiled to a {@code JavaIdentifier} or {@code null}, if {@code str} is {@code null}.
1916 *
1917 * @throws ParseException if compiling {@code str} to a {@code JavaIdentifier} fails.
1918 *
1919 * @since 1.3
1920 *
1921 * @see JavaIdentifier#normalize(java.lang.String, org.jomc.model.JavaIdentifier.NormalizationMode)
1922 * @see org.jomc.model.JavaIdentifier.NormalizationMode#CONSTANT_NAME_CONVENTION
1923 */
1924 public JavaIdentifier toJavaConstantName( final String str ) throws ParseException
1925 {
1926 JavaIdentifier constantName = null;
1927
1928 if ( str != null )
1929 {
1930 constantName = JavaIdentifier.normalize( str, JavaIdentifier.NormalizationMode.CONSTANT_NAME_CONVENTION );
1931 }
1932
1933 return constantName;
1934 }
1935
1936 /**
1937 * Compiles a string to a Java method name.
1938 *
1939 * @param str The string to compile or {@code null}.
1940 *
1941 * @return {@code str} compiled to a {@code JavaIdentifier} or {@code null}, if {@code str} is {@code null}.
1942 *
1943 * @throws ParseException if compiling {@code str} to a {@code JavaIdentifier} fails.
1944 *
1945 * @since 1.4
1946 *
1947 * @see JavaIdentifier#normalize(java.lang.String, org.jomc.model.JavaIdentifier.NormalizationMode)
1948 * @see org.jomc.model.JavaIdentifier.NormalizationMode#METHOD_NAME_CONVENTION
1949 */
1950 public JavaIdentifier toJavaMethodName( final String str ) throws ParseException
1951 {
1952 JavaIdentifier variableName = null;
1953
1954 if ( str != null )
1955 {
1956 variableName =
1957 JavaIdentifier.normalize( str, JavaIdentifier.NormalizationMode.METHOD_NAME_CONVENTION );
1958
1959 }
1960
1961 return variableName;
1962 }
1963
1964 /**
1965 * Compiles a string to a Java variable name.
1966 *
1967 * @param str The string to compile or {@code null}.
1968 *
1969 * @return {@code str} compiled to a {@code JavaIdentifier} or {@code null}, if {@code str} is {@code null}.
1970 *
1971 * @throws ParseException if compiling {@code str} to a {@code JavaIdentifier} fails.
1972 *
1973 * @since 1.4
1974 *
1975 * @see JavaIdentifier#normalize(java.lang.String, org.jomc.model.JavaIdentifier.NormalizationMode)
1976 * @see org.jomc.model.JavaIdentifier.NormalizationMode#VARIABLE_NAME_CONVENTION
1977 */
1978 public JavaIdentifier toJavaVariableName( final String str ) throws ParseException
1979 {
1980 JavaIdentifier variableName = null;
1981
1982 if ( str != null )
1983 {
1984 variableName =
1985 JavaIdentifier.normalize( str, JavaIdentifier.NormalizationMode.VARIABLE_NAME_CONVENTION );
1986
1987 }
1988
1989 return variableName;
1990 }
1991
1992 /**
1993 * Gets a flag indicating the type referenced by a given specification is located in an unnamed Java package.
1994 *
1995 * @param specification The specification to query.
1996 *
1997 * @return {@code true}, if the type referenced by {@code specification} is located in an unnamed Java package;
1998 * {@code false}, if the specification does not reference a type or if the referenced type is not located in an
1999 * unnamed Java package.
2000 *
2001 * @throws NullPointerException if {@code specification} is {@code null}.
2002 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
2003 *
2004 * @see Specification#getJavaTypeName()
2005 * @see JavaTypeName#isUnnamedPackage()
2006 *
2007 * @deprecated As of JOMC 1.4, please use method {@link Specification#getJavaTypeName()}. This method will be
2008 * removed in JOMC 2.0.
2009 */
2010 @Deprecated
2011 public boolean isJavaDefaultPackage( final Specification specification ) throws ModelObjectException
2012 {
2013 if ( specification == null )
2014 {
2015 throw new NullPointerException( "specification" );
2016 }
2017
2018 final JavaTypeName javaTypeName = specification.getJavaTypeName();
2019 return javaTypeName != null && javaTypeName.isUnnamedPackage();
2020 }
2021
2022 /**
2023 * Gets a flag indicating the type referenced by a given implementation is located in an unnamed Java package.
2024 *
2025 * @param implementation The implementation to query.
2026 *
2027 * @return {@code true}, if the type referenced by {@code implementation} is located in an unnamed Java package;
2028 * {@code false}, if the implementation does not reference a type or if the referenced type is not located in an
2029 * unnamed Java package.
2030 *
2031 * @throws NullPointerException if {@code implementation} is {@code null}.
2032 * @throws ModelObjectException if compiling the name of the referenced type to a {@code JavaTypeName} fails.
2033 *
2034 * @see Implementation#getJavaTypeName()
2035 * @see JavaTypeName#isUnnamedPackage()
2036 *
2037 * @deprecated As of JOMC 1.4, please use method {@link Implementation#getJavaTypeName()}. This method will be
2038 * removed in JOMC 2.0.
2039 */
2040 @Deprecated
2041 public boolean isJavaDefaultPackage( final Implementation implementation ) throws ModelObjectException
2042 {
2043 if ( implementation == null )
2044 {
2045 throw new NullPointerException( "implementation" );
2046 }
2047
2048 final JavaTypeName javaTypeName = implementation.getJavaTypeName();
2049 return javaTypeName != null && javaTypeName.isUnnamedPackage();
2050 }
2051
2052 /**
2053 * Formats a string to a HTML string with HTML entities.
2054 *
2055 * @param str The string to format to a HTML string with HTML entities or {@code null}.
2056 *
2057 * @return {@code str} formatted to a HTML string with HTML entities or {@code null}.
2058 *
2059 * @since 1.2
2060 */
2061 public String getHtmlString( final String str )
2062 {
2063 return str != null ? str.replace( "&", "&" ).replace( "<", "<" ).replace( ">", ">" ).
2064 replace( "\"", """ ).replace( "*", "∗" ) : null;
2065
2066 }
2067
2068 /**
2069 * Formats a string to a XML string with XML entities.
2070 *
2071 * @param str The string to format to a XML string with XML entities or {@code null}.
2072 *
2073 * @return {@code str} formatted to a XML string with XML entities or {@code null}.
2074 *
2075 * @see StringEscapeUtils#escapeXml(java.lang.String)
2076 *
2077 * @since 1.2
2078 */
2079 public String getXmlString( final String str )
2080 {
2081 return StringEscapeUtils.escapeXml( str );
2082 }
2083
2084 /**
2085 * Formats a string to a JavaScript string applying JavaScript string rules.
2086 *
2087 * @param str The string to format to a JavaScript string by applying JavaScript string rules or {@code null}.
2088 *
2089 * @return {@code str} formatted to a JavaScript string with JavaScript string rules applied or {@code null}.
2090 *
2091 * @see StringEscapeUtils#escapeJavaScript(java.lang.String)
2092 *
2093 * @since 1.2
2094 */
2095 public String getJavaScriptString( final String str )
2096 {
2097 return StringEscapeUtils.escapeJavaScript( str );
2098 }
2099
2100 /**
2101 * Formats a string to a SQL string.
2102 *
2103 * @param str The string to format to a SQL string or {@code null}.
2104 *
2105 * @return {@code str} formatted to a SQL string or {@code null}.
2106 *
2107 * @see StringEscapeUtils#escapeSql(java.lang.String)
2108 *
2109 * @since 1.2
2110 */
2111 public String getSqlString( final String str )
2112 {
2113 return StringEscapeUtils.escapeSql( str );
2114 }
2115
2116 /**
2117 * Formats a string to a CSV string.
2118 *
2119 * @param str The string to format to a CSV string or {@code null}.
2120 *
2121 * @return {@code str} formatted to a CSV string or {@code null}.
2122 *
2123 * @see StringEscapeUtils#escapeCsv(java.lang.String)
2124 *
2125 * @since 1.2
2126 */
2127 public String getCsvString( final String str )
2128 {
2129 return StringEscapeUtils.escapeCsv( str );
2130 }
2131
2132 /**
2133 * Formats a {@code Boolean} to a string.
2134 *
2135 * @param b The {@code Boolean} to format to a string or {@code null}.
2136 *
2137 * @return {@code b} formatted to a string.
2138 *
2139 * @see #getLocale()
2140 *
2141 * @since 1.2
2142 */
2143 public String getBooleanString( final Boolean b )
2144 {
2145 return ResourceBundle.getBundle( JomcTool.class.getName(), this.getLocale() ).
2146 getString( b ? "booleanStringTrue" : "booleanStringFalse" );
2147
2148 }
2149
2150 /**
2151 * Gets the display language of a given language code.
2152 *
2153 * @param language The language code to get the display language of.
2154 *
2155 * @return The display language of {@code language}.
2156 *
2157 * @throws NullPointerException if {@code language} is {@code null}.
2158 */
2159 public String getDisplayLanguage( final String language )
2160 {
2161 if ( language == null )
2162 {
2163 throw new NullPointerException( "language" );
2164 }
2165
2166 final Locale l = new Locale( language );
2167 return l.getDisplayLanguage( l );
2168 }
2169
2170 /**
2171 * Formats a calendar instance to a string.
2172 *
2173 * @param calendar The calendar to format to a string.
2174 *
2175 * @return The date of {@code calendar} formatted using a short format style pattern.
2176 *
2177 * @throws NullPointerException if {@code calendar} is {@code null}.
2178 *
2179 * @see DateFormat#SHORT
2180 */
2181 public String getShortDate( final Calendar calendar )
2182 {
2183 if ( calendar == null )
2184 {
2185 throw new NullPointerException( "calendar" );
2186 }
2187
2188 return DateFormat.getDateInstance( DateFormat.SHORT, this.getLocale() ).format( calendar.getTime() );
2189 }
2190
2191 /**
2192 * Formats a calendar instance to a string.
2193 *
2194 * @param calendar The calendar to format to a string.
2195 *
2196 * @return The date of {@code calendar} formatted using a medium format style pattern.
2197 *
2198 * @throws NullPointerException if {@code calendar} is {@code null}.
2199 *
2200 * @see DateFormat#MEDIUM
2201 *
2202 * @since 1.2
2203 */
2204 public String getMediumDate( final Calendar calendar )
2205 {
2206 if ( calendar == null )
2207 {
2208 throw new NullPointerException( "calendar" );
2209 }
2210
2211 return DateFormat.getDateInstance( DateFormat.MEDIUM, this.getLocale() ).format( calendar.getTime() );
2212 }
2213
2214 /**
2215 * Formats a calendar instance to a string.
2216 *
2217 * @param calendar The calendar to format to a string.
2218 *
2219 * @return The date of {@code calendar} formatted using a long format style pattern.
2220 *
2221 * @throws NullPointerException if {@code calendar} is {@code null}.
2222 *
2223 * @see DateFormat#LONG
2224 */
2225 public String getLongDate( final Calendar calendar )
2226 {
2227 if ( calendar == null )
2228 {
2229 throw new NullPointerException( "calendar" );
2230 }
2231
2232 return DateFormat.getDateInstance( DateFormat.LONG, this.getLocale() ).format( calendar.getTime() );
2233 }
2234
2235 /**
2236 * Formats a calendar instance to a string.
2237 *
2238 * @param calendar The calendar to format to a string.
2239 *
2240 * @return The date of {@code calendar} formatted using an ISO-8601 format style.
2241 *
2242 * @throws NullPointerException if {@code calendar} is {@code null}.
2243 *
2244 * @see SimpleDateFormat yyyy-DDD
2245 *
2246 * @since 1.2
2247 */
2248 public String getIsoDate( final Calendar calendar )
2249 {
2250 if ( calendar == null )
2251 {
2252 throw new NullPointerException( "calendar" );
2253 }
2254
2255 return new SimpleDateFormat( "yyyy-DDD", this.getLocale() ).format( calendar.getTime() );
2256 }
2257
2258 /**
2259 * Formats a calendar instance to a string.
2260 *
2261 * @param calendar The calendar to format to a string.
2262 *
2263 * @return The time of {@code calendar} formatted using a short format style pattern.
2264 *
2265 * @throws NullPointerException if {@code calendar} is {@code null}.
2266 *
2267 * @see DateFormat#SHORT
2268 */
2269 public String getShortTime( final Calendar calendar )
2270 {
2271 if ( calendar == null )
2272 {
2273 throw new NullPointerException( "calendar" );
2274 }
2275
2276 return DateFormat.getTimeInstance( DateFormat.SHORT, this.getLocale() ).format( calendar.getTime() );
2277 }
2278
2279 /**
2280 * Formats a calendar instance to a string.
2281 *
2282 * @param calendar The calendar to format to a string.
2283 *
2284 * @return The time of {@code calendar} formatted using a medium format style pattern.
2285 *
2286 * @throws NullPointerException if {@code calendar} is {@code null}.
2287 *
2288 * @see DateFormat#MEDIUM
2289 *
2290 * @since 1.2
2291 */
2292 public String getMediumTime( final Calendar calendar )
2293 {
2294 if ( calendar == null )
2295 {
2296 throw new NullPointerException( "calendar" );
2297 }
2298
2299 return DateFormat.getTimeInstance( DateFormat.MEDIUM, this.getLocale() ).format( calendar.getTime() );
2300 }
2301
2302 /**
2303 * Formats a calendar instance to a string.
2304 *
2305 * @param calendar The calendar to format to a string.
2306 *
2307 * @return The time of {@code calendar} formatted using a long format style pattern.
2308 *
2309 * @throws NullPointerException if {@code calendar} is {@code null}.
2310 *
2311 * @see DateFormat#LONG
2312 */
2313 public String getLongTime( final Calendar calendar )
2314 {
2315 if ( calendar == null )
2316 {
2317 throw new NullPointerException( "calendar" );
2318 }
2319
2320 return DateFormat.getTimeInstance( DateFormat.LONG, this.getLocale() ).format( calendar.getTime() );
2321 }
2322
2323 /**
2324 * Formats a calendar instance to a string.
2325 *
2326 * @param calendar The calendar to format to a string.
2327 *
2328 * @return The time of {@code calendar} formatted using an ISO-8601 format style.
2329 *
2330 * @throws NullPointerException if {@code calendar} is {@code null}.
2331 *
2332 * @see SimpleDateFormat HH:mm
2333 *
2334 * @since 1.2
2335 */
2336 public String getIsoTime( final Calendar calendar )
2337 {
2338 if ( calendar == null )
2339 {
2340 throw new NullPointerException( "calendar" );
2341 }
2342
2343 return new SimpleDateFormat( "HH:mm", this.getLocale() ).format( calendar.getTime() );
2344 }
2345
2346 /**
2347 * Formats a calendar instance to a string.
2348 *
2349 * @param calendar The calendar to format to a string.
2350 *
2351 * @return The date and time of {@code calendar} formatted using a short format style pattern.
2352 *
2353 * @throws NullPointerException if {@code calendar} is {@code null}.
2354 *
2355 * @see DateFormat#SHORT
2356 */
2357 public String getShortDateTime( final Calendar calendar )
2358 {
2359 if ( calendar == null )
2360 {
2361 throw new NullPointerException( "calendar" );
2362 }
2363
2364 return DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT, this.getLocale() ).
2365 format( calendar.getTime() );
2366
2367 }
2368
2369 /**
2370 * Formats a calendar instance to a string.
2371 *
2372 * @param calendar The calendar to format to a string.
2373 *
2374 * @return The date and time of {@code calendar} formatted using a medium format style pattern.
2375 *
2376 * @throws NullPointerException if {@code calendar} is {@code null}.
2377 *
2378 * @see DateFormat#MEDIUM
2379 *
2380 * @since 1.2
2381 */
2382 public String getMediumDateTime( final Calendar calendar )
2383 {
2384 if ( calendar == null )
2385 {
2386 throw new NullPointerException( "calendar" );
2387 }
2388
2389 return DateFormat.getDateTimeInstance( DateFormat.MEDIUM, DateFormat.MEDIUM, this.getLocale() ).
2390 format( calendar.getTime() );
2391
2392 }
2393
2394 /**
2395 * Formats a calendar instance to a string.
2396 *
2397 * @param calendar The calendar to format to a string.
2398 *
2399 * @return The date and time of {@code calendar} formatted using a long format style pattern.
2400 *
2401 * @throws NullPointerException if {@code calendar} is {@code null}.
2402 *
2403 * @see DateFormat#LONG
2404 */
2405 public String getLongDateTime( final Calendar calendar )
2406 {
2407 if ( calendar == null )
2408 {
2409 throw new NullPointerException( "calendar" );
2410 }
2411
2412 return DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG, this.getLocale() ).
2413 format( calendar.getTime() );
2414
2415 }
2416
2417 /**
2418 * Formats a calendar instance to a string.
2419 *
2420 * @param calendar The calendar to format to a string.
2421 *
2422 * @return The date and time of {@code calendar} formatted using a ISO-8601 format style.
2423 *
2424 * @throws NullPointerException if {@code calendar} is {@code null}.
2425 *
2426 * @see SimpleDateFormat yyyy-MM-dd'T'HH:mm:ssZ
2427 *
2428 * @since 1.2
2429 */
2430 public String getIsoDateTime( final Calendar calendar )
2431 {
2432 if ( calendar == null )
2433 {
2434 throw new NullPointerException( "calendar" );
2435 }
2436
2437 return Closeable.class.isAssignableFrom( ClassLoader.class )
2438 ? new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssXXX", this.getLocale() ).format( calendar.getTime() )
2439 : new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssZ", this.getLocale() ).format( calendar.getTime() );
2440
2441 }
2442
2443 /**
2444 * Gets a string describing the range of years for given calendars.
2445 *
2446 * @param start The start of the range.
2447 * @param end The end of the range.
2448 *
2449 * @return Formatted range of the years of {@code start} and {@code end} (e.g. {@code "start - end"}).
2450 *
2451 * @throws NullPointerException if {@code start} or {@code end} is {@code null}.
2452 */
2453 public String getYears( final Calendar start, final Calendar end )
2454 {
2455 if ( start == null )
2456 {
2457 throw new NullPointerException( "start" );
2458 }
2459 if ( end == null )
2460 {
2461 throw new NullPointerException( "end" );
2462 }
2463
2464 final Format yearFormat = new SimpleDateFormat( "yyyy", this.getLocale() );
2465 final int s = start.get( Calendar.YEAR );
2466 final int e = end.get( Calendar.YEAR );
2467 final StringBuilder years = new StringBuilder();
2468
2469 if ( s != e )
2470 {
2471 if ( s < e )
2472 {
2473 years.append( yearFormat.format( start.getTime() ) ).append( " - " ).
2474 append( yearFormat.format( end.getTime() ) );
2475
2476 }
2477 else
2478 {
2479 years.append( yearFormat.format( end.getTime() ) ).append( " - " ).
2480 append( yearFormat.format( start.getTime() ) );
2481
2482 }
2483 }
2484 else
2485 {
2486 years.append( yearFormat.format( start.getTime() ) );
2487 }
2488
2489 return years.toString();
2490 }
2491
2492 /**
2493 * Gets the model of the instance.
2494 *
2495 * @return The model of the instance.
2496 *
2497 * @see #getModules()
2498 * @see #setModel(org.jomc.modlet.Model)
2499 */
2500 public final Model getModel()
2501 {
2502 if ( this.model == null )
2503 {
2504 this.model = new Model();
2505 this.model.setIdentifier( ModelObject.MODEL_PUBLIC_ID );
2506 }
2507
2508 return this.model;
2509 }
2510
2511 /**
2512 * Sets the model of the instance.
2513 *
2514 * @param value The new model of the instance or {@code null}.
2515 *
2516 * @see #getModel()
2517 */
2518 public final void setModel( final Model value )
2519 {
2520 this.model = value;
2521 }
2522
2523 /**
2524 * Gets the modules of the model of the instance.
2525 *
2526 * @return The modules of the model of the instance or {@code null}, if no modules are found.
2527 *
2528 * @see #getModel()
2529 * @see #setModel(org.jomc.modlet.Model)
2530 */
2531 public final Modules getModules()
2532 {
2533 return ModelHelper.getModules( this.getModel() );
2534 }
2535
2536 /**
2537 * Gets the {@code VelocityEngine} of the instance.
2538 *
2539 * @return The {@code VelocityEngine} of the instance.
2540 *
2541 * @see #setVelocityEngine(org.apache.velocity.app.VelocityEngine)
2542 */
2543 public final VelocityEngine getVelocityEngine()
2544 {
2545 if ( this.velocityEngine == null )
2546 {
2547 /**
2548 * {@code LogChute} logging to the listeners of the tool.
2549 */
2550 class JomcLogChute implements LogChute
2551 {
2552
2553 JomcLogChute()
2554 {
2555 super();
2556 }
2557
2558 public void init( final RuntimeServices runtimeServices ) throws Exception
2559 {
2560 }
2561
2562 public void log( final int level, final String message )
2563 {
2564 this.log( level, message, null );
2565 }
2566
2567 public void log( final int level, final String message, final Throwable throwable )
2568 {
2569 JomcTool.this.log( Level.FINEST, message, throwable );
2570 }
2571
2572 public boolean isLevelEnabled( final int level )
2573 {
2574 return isLoggable( Level.FINEST );
2575 }
2576
2577 }
2578
2579 final VelocityEngine engine = new VelocityEngine();
2580 engine.setProperty( RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE.toString() );
2581 engine.setProperty( RuntimeConstants.VM_ARGUMENTS_STRICT, Boolean.TRUE.toString() );
2582 engine.setProperty( RuntimeConstants.STRICT_MATH, Boolean.TRUE.toString() );
2583 engine.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new JomcLogChute() );
2584
2585 engine.setProperty( RuntimeConstants.RESOURCE_LOADER, "class" );
2586 engine.setProperty( "class.resource.loader.class", ClasspathResourceLoader.class.getName() );
2587 engine.setProperty( "class.resource.loader.cache", Boolean.TRUE.toString() );
2588
2589 if ( this.getTemplateLocation() != null )
2590 {
2591 engine.setProperty( RuntimeConstants.RESOURCE_LOADER, "class,url" );
2592 engine.setProperty( "url.resource.loader.class", URLResourceLoader.class.getName() );
2593 engine.setProperty( "url.resource.loader.cache", Boolean.TRUE.toString() );
2594 engine.setProperty( "url.resource.loader.root", this.getTemplateLocation().toExternalForm() );
2595 engine.setProperty( "url.resource.loader.timeout", Integer.toString( 60000 ) );
2596 }
2597
2598 this.velocityEngine = engine;
2599 this.defaultVelocityEngine = true;
2600 }
2601
2602 return this.velocityEngine;
2603 }
2604
2605 /**
2606 * Sets the {@code VelocityEngine} of the instance.
2607 *
2608 * @param value The new {@code VelocityEngine} of the instance or {@code null}.
2609 *
2610 * @see #getVelocityEngine()
2611 */
2612 public final void setVelocityEngine( final VelocityEngine value )
2613 {
2614 this.velocityEngine = value;
2615 this.defaultVelocityEngine = false;
2616 }
2617
2618 /**
2619 * Gets a new velocity context used for merging templates.
2620 *
2621 * @return A new velocity context used for merging templates.
2622 *
2623 * @throws IOException if creating a new context instance fails.
2624 *
2625 * @see #getTemplateParameters()
2626 */
2627 public VelocityContext getVelocityContext() throws IOException
2628 {
2629 final Calendar now = Calendar.getInstance();
2630 final VelocityContext ctx =
2631 new VelocityContext( new HashMap<String, Object>( this.getTemplateParameters() ) );
2632
2633 this.mergeTemplateProfileContextProperties( this.getTemplateProfile(), this.getLocale().getLanguage(), ctx );
2634 this.mergeTemplateProfileContextProperties( this.getTemplateProfile(), null, ctx );
2635
2636 final Model clonedModel = this.getModel().clone();
2637 final Modules clonedModules = ModelHelper.getModules( clonedModel );
2638 assert clonedModules != null : "Unexpected missing modules for model '" + clonedModel.getIdentifier() + "'.";
2639
2640 ctx.put( "model", clonedModel );
2641 ctx.put( "modules", clonedModules );
2642 ctx.put( "imodel", new InheritanceModel( clonedModules ) );
2643 ctx.put( "tool", this );
2644 ctx.put( "toolName", this.getClass().getName() );
2645 ctx.put( "toolVersion", getMessage( "projectVersion" ) );
2646 ctx.put( "toolUrl", getMessage( "projectUrl" ) );
2647 ctx.put( "calendar", now.getTime() );
2648 ctx.put( "now",
2649 Closeable.class.isAssignableFrom( ClassLoader.class )
2650 ? new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", this.getLocale() ).format( now.getTime() )
2651 : new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ", this.getLocale() ).format( now.getTime() ) );
2652
2653 ctx.put( "year", new SimpleDateFormat( "yyyy", this.getLocale() ).format( now.getTime() ) );
2654 ctx.put( "month", new SimpleDateFormat( "MM", this.getLocale() ).format( now.getTime() ) );
2655 ctx.put( "day", new SimpleDateFormat( "dd", this.getLocale() ).format( now.getTime() ) );
2656 ctx.put( "hour", new SimpleDateFormat( "HH", this.getLocale() ).format( now.getTime() ) );
2657 ctx.put( "minute", new SimpleDateFormat( "mm", this.getLocale() ).format( now.getTime() ) );
2658 ctx.put( "second", new SimpleDateFormat( "ss", this.getLocale() ).format( now.getTime() ) );
2659 ctx.put( "timezone",
2660 Closeable.class.isAssignableFrom( ClassLoader.class )
2661 ? new SimpleDateFormat( "XXX", this.getLocale() ).format( now.getTime() )
2662 : new SimpleDateFormat( "Z", this.getLocale() ).format( now.getTime() ) );
2663
2664 ctx.put( "shortDate", this.getShortDate( now ) );
2665 ctx.put( "mediumDate", this.getMediumDate( now ) );
2666 ctx.put( "longDate", this.getLongDate( now ) );
2667 ctx.put( "isoDate", this.getIsoDate( now ) );
2668 ctx.put( "shortTime", this.getShortTime( now ) );
2669 ctx.put( "mediumTime", this.getMediumTime( now ) );
2670 ctx.put( "longTime", this.getLongTime( now ) );
2671 ctx.put( "isoTime", this.getIsoTime( now ) );
2672 ctx.put( "shortDateTime", this.getShortDateTime( now ) );
2673 ctx.put( "mediumDateTime", this.getMediumDateTime( now ) );
2674 ctx.put( "longDateTime", this.getLongDateTime( now ) );
2675 ctx.put( "isoDateTime", this.getIsoDateTime( now ) );
2676
2677 return ctx;
2678 }
2679
2680 /**
2681 * Gets the template parameters of the instance.
2682 * <p>
2683 * This accessor method returns a reference to the live map, not a snapshot. Therefore any modification you make
2684 * to the returned map will be present inside the object. This is why there is no {@code set} method for the
2685 * template parameters property.
2686 * </p>
2687 *
2688 * @return The template parameters of the instance.
2689 *
2690 * @see #getVelocityContext()
2691 *
2692 * @since 1.2
2693 */
2694 public final Map<String, Object> getTemplateParameters()
2695 {
2696 return this.templateParameters;
2697 }
2698
2699 /**
2700 * Gets the location to search for templates in addition to searching the class path.
2701 *
2702 * @return The location to search for templates in addition to searching the class path or {@code null}.
2703 *
2704 * @see #setTemplateLocation(java.net.URL)
2705 *
2706 * @since 1.2
2707 */
2708 public final URL getTemplateLocation()
2709 {
2710 return this.templateLocation;
2711 }
2712
2713 /**
2714 * Sets the location to search for templates in addition to searching the class path.
2715 *
2716 * @param value The new location to search for templates in addition to searching the class path or {@code null}.
2717 *
2718 * @see #getTemplateLocation()
2719 *
2720 * @since 1.2
2721 */
2722 public final void setTemplateLocation( final URL value )
2723 {
2724 this.templateLocation = value;
2725 this.templateProfileContextPropertiesCache = null;
2726 this.templateProfilePropertiesCache = null;
2727
2728 if ( this.defaultVelocityEngine )
2729 {
2730 this.setVelocityEngine( null );
2731 }
2732 }
2733
2734 /**
2735 * Gets the encoding to use for reading templates.
2736 *
2737 * @return The encoding to use for reading templates.
2738 *
2739 * @see #setTemplateEncoding(java.lang.String)
2740 *
2741 * @deprecated As of JOMC 1.3, replaced by method {@link #getDefaultTemplateEncoding()}. This method will be removed
2742 * in JOMC 2.0.
2743 */
2744 @Deprecated
2745 public final String getTemplateEncoding()
2746 {
2747 return this.getDefaultTemplateEncoding();
2748 }
2749
2750 /**
2751 * Sets the encoding to use for reading templates.
2752 *
2753 * @param value The new encoding to use for reading templates or {@code null}.
2754 *
2755 * @see #getTemplateEncoding()
2756 *
2757 * @deprecated As of JOMC 1.3, replaced by method {@link #setDefaultTemplateEncoding(java.lang.String)}. This method
2758 * will be removed in JOMC 2.0.
2759 */
2760 @Deprecated
2761 public final void setTemplateEncoding( final String value )
2762 {
2763 this.setDefaultTemplateEncoding( value );
2764 }
2765
2766 /**
2767 * Gets the default encoding used for reading templates.
2768 *
2769 * @return The default encoding used for reading templates.
2770 *
2771 * @see #setDefaultTemplateEncoding(java.lang.String)
2772 *
2773 * @since 1.3
2774 */
2775 public final String getDefaultTemplateEncoding()
2776 {
2777 if ( this.defaultTemplateEncoding == null )
2778 {
2779 this.defaultTemplateEncoding = getMessage( "buildSourceEncoding" );
2780
2781 if ( this.isLoggable( Level.CONFIG ) )
2782 {
2783 this.log( Level.CONFIG, getMessage( "defaultTemplateEncoding", this.defaultTemplateEncoding ), null );
2784 }
2785 }
2786
2787 return this.defaultTemplateEncoding;
2788 }
2789
2790 /**
2791 * Sets the default encoding to use for reading templates.
2792 *
2793 * @param value The new default encoding to use for reading templates or {@code null}.
2794 *
2795 * @see #getDefaultTemplateEncoding()
2796 *
2797 * @since 1.3
2798 */
2799 public final void setDefaultTemplateEncoding( final String value )
2800 {
2801 this.defaultTemplateEncoding = value;
2802 this.templateCache = null;
2803 }
2804
2805 /**
2806 * Gets the template encoding of a given template profile.
2807 *
2808 * @param tp The template profile to get the template encoding of.
2809 *
2810 * @return The template encoding of the template profile identified by {@code tp} or the default template encoding
2811 * if no such encoding is defined.
2812 *
2813 * @throws NullPointerException if {@code tp} is {@code null}.
2814 *
2815 * @see #getDefaultTemplateEncoding()
2816 *
2817 * @since 1.3
2818 */
2819 public final String getTemplateEncoding( final String tp )
2820 {
2821 if ( tp == null )
2822 {
2823 throw new NullPointerException( "tp" );
2824 }
2825
2826 String te = null;
2827
2828 try
2829 {
2830 te = this.getTemplateProfileProperties( tp ).getProperty( TEMPLATE_ENCODING_PROFILE_PROPERTY_NAME );
2831 }
2832 catch ( final IOException e )
2833 {
2834 if ( this.isLoggable( Level.SEVERE ) )
2835 {
2836 this.log( Level.SEVERE, getMessage( e ), e );
2837 }
2838 }
2839
2840 return te != null ? te : this.getDefaultTemplateEncoding();
2841 }
2842
2843 /**
2844 * Gets the encoding to use for reading files.
2845 *
2846 * @return The encoding to use for reading files.
2847 *
2848 * @see #setInputEncoding(java.lang.String)
2849 */
2850 public final String getInputEncoding()
2851 {
2852 if ( this.inputEncoding == null )
2853 {
2854 this.inputEncoding = new InputStreamReader( new ByteArrayInputStream( NO_BYTES ) ).getEncoding();
2855
2856 if ( this.isLoggable( Level.CONFIG ) )
2857 {
2858 this.log( Level.CONFIG, getMessage( "defaultInputEncoding", this.inputEncoding ), null );
2859 }
2860 }
2861
2862 return this.inputEncoding;
2863 }
2864
2865 /**
2866 * Sets the encoding to use for reading files.
2867 *
2868 * @param value The new encoding to use for reading files or {@code null}.
2869 *
2870 * @see #getInputEncoding()
2871 */
2872 public final void setInputEncoding( final String value )
2873 {
2874 this.inputEncoding = value;
2875 }
2876
2877 /**
2878 * Gets the encoding to use for writing files.
2879 *
2880 * @return The encoding to use for writing files.
2881 *
2882 * @see #setOutputEncoding(java.lang.String)
2883 */
2884 public final String getOutputEncoding()
2885 {
2886 if ( this.outputEncoding == null )
2887 {
2888 this.outputEncoding = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding();
2889
2890 if ( this.isLoggable( Level.CONFIG ) )
2891 {
2892 this.log( Level.CONFIG, getMessage( "defaultOutputEncoding", this.outputEncoding ), null );
2893 }
2894 }
2895
2896 return this.outputEncoding;
2897 }
2898
2899 /**
2900 * Sets the encoding to use for writing files.
2901 *
2902 * @param value The encoding to use for writing files or {@code null}.
2903 *
2904 * @see #getOutputEncoding()
2905 */
2906 public final void setOutputEncoding( final String value )
2907 {
2908 this.outputEncoding = value;
2909 }
2910
2911 /**
2912 * Gets the default template profile.
2913 * <p>
2914 * The default template profile is the implicit parent profile of any template profile not specifying a parent
2915 * template profile.
2916 * </p>
2917 *
2918 * @return The default template profile.
2919 *
2920 * @see #setDefaultTemplateProfile(java.lang.String)
2921 *
2922 * @deprecated The {@code static} modifier of this method and support to setup the default template profile using
2923 * a system property will be removed in version 2.0.
2924 */
2925 @Deprecated
2926 public static String getDefaultTemplateProfile()
2927 {
2928 if ( defaultTemplateProfile == null )
2929 {
2930 defaultTemplateProfile = System.getProperty( "org.jomc.tools.JomcTool.defaultTemplateProfile",
2931 DEFAULT_TEMPLATE_PROFILE );
2932
2933 }
2934
2935 return defaultTemplateProfile;
2936 }
2937
2938 /**
2939 * Sets the default template profile.
2940 *
2941 * @param value The new default template profile or {@code null}.
2942 *
2943 * @see #getDefaultTemplateProfile()
2944 *
2945 * @deprecated The {@code static} modifier of this method will be removed in version 2.0.
2946 */
2947 @Deprecated
2948 public static void setDefaultTemplateProfile( final String value )
2949 {
2950 defaultTemplateProfile = value;
2951 }
2952
2953 /**
2954 * Gets the template profile of the instance.
2955 *
2956 * @return The template profile of the instance.
2957 *
2958 * @see #getDefaultTemplateProfile()
2959 * @see #setTemplateProfile(java.lang.String)
2960 */
2961 public final String getTemplateProfile()
2962 {
2963 if ( this.templateProfile == null )
2964 {
2965 this.templateProfile = getDefaultTemplateProfile();
2966
2967 if ( this.isLoggable( Level.CONFIG ) )
2968 {
2969 this.log( Level.CONFIG, getMessage( "defaultTemplateProfile", this.templateProfile ), null );
2970 }
2971 }
2972
2973 return this.templateProfile;
2974 }
2975
2976 /**
2977 * Sets the template profile of the instance.
2978 *
2979 * @param value The new template profile of the instance or {@code null}.
2980 *
2981 * @see #getTemplateProfile()
2982 */
2983 public final void setTemplateProfile( final String value )
2984 {
2985 this.templateProfile = value;
2986 }
2987
2988 /**
2989 * Gets the parent template profile of a given template profile.
2990 *
2991 * @param tp The template profile to get the parent template profile of.
2992 *
2993 * @return The parent template profile of the template profile identified by {@code tp}; the default template
2994 * profile, if no such parent template profile is defined; {@code null}, if {@code tp} denotes the default template
2995 * profile.
2996 *
2997 * @throws NullPointerException if {@code tp} is {@code null}.
2998 *
2999 * @see #getDefaultTemplateProfile()
3000 *
3001 * @since 1.3
3002 */
3003 public final String getParentTemplateProfile( final String tp )
3004 {
3005 if ( tp == null )
3006 {
3007 throw new NullPointerException( "tp" );
3008 }
3009
3010 String parentTemplateProfile = null;
3011
3012 try
3013 {
3014 parentTemplateProfile =
3015 this.getTemplateProfileProperties( tp ).getProperty( PARENT_TEMPLATE_PROFILE_PROPERTY_NAME );
3016
3017 }
3018 catch ( final IOException e )
3019 {
3020 if ( this.isLoggable( Level.SEVERE ) )
3021 {
3022 this.log( Level.SEVERE, getMessage( e ), e );
3023 }
3024 }
3025
3026 return parentTemplateProfile != null ? parentTemplateProfile
3027 : tp.equals( this.getDefaultTemplateProfile() ) ? null : this.getDefaultTemplateProfile();
3028
3029 }
3030
3031 /**
3032 * Gets the indentation string of the instance.
3033 *
3034 * @return The indentation string of the instance.
3035 *
3036 * @see #setIndentation(java.lang.String)
3037 */
3038 public final String getIndentation()
3039 {
3040 if ( this.indentation == null )
3041 {
3042 this.indentation = " ";
3043
3044 if ( this.isLoggable( Level.CONFIG ) )
3045 {
3046 this.log( Level.CONFIG, getMessage( "defaultIndentation",
3047 StringEscapeUtils.escapeJava( this.indentation ) ), null );
3048
3049 }
3050 }
3051
3052 return this.indentation;
3053 }
3054
3055 /**
3056 * Gets an indentation string for a given indentation level.
3057 *
3058 * @param level The indentation level to get an indentation string for.
3059 *
3060 * @return The indentation string for {@code level}.
3061 *
3062 * @throws IllegalArgumentException if {@code level} is negative.
3063 *
3064 * @see #getIndentation()
3065 */
3066 public final String getIndentation( final int level )
3067 {
3068 if ( level < 0 )
3069 {
3070 throw new IllegalArgumentException( Integer.toString( level ) );
3071 }
3072
3073 Map<String, String> map = this.indentationCache == null ? null : this.indentationCache.get();
3074
3075 if ( map == null )
3076 {
3077 map = new ConcurrentHashMap<String, String>( 8 );
3078 this.indentationCache = new SoftReference<Map<String, String>>( map );
3079 }
3080
3081 final String key = this.getIndentation() + "|" + level;
3082 String idt = map.get( key );
3083
3084 if ( idt == null )
3085 {
3086 final StringBuilder b = new StringBuilder( this.getIndentation().length() * level );
3087
3088 for ( int i = level; i > 0; i-- )
3089 {
3090 b.append( this.getIndentation() );
3091 }
3092
3093 idt = b.toString();
3094 map.put( key, idt );
3095 }
3096
3097 return idt;
3098 }
3099
3100 /**
3101 * Sets the indentation string of the instance.
3102 *
3103 * @param value The new indentation string of the instance or {@code null}.
3104 *
3105 * @see #getIndentation()
3106 */
3107 public final void setIndentation( final String value )
3108 {
3109 this.indentation = value;
3110 }
3111
3112 /**
3113 * Gets the line separator of the instance.
3114 *
3115 * @return The line separator of the instance.
3116 *
3117 * @see #setLineSeparator(java.lang.String)
3118 */
3119 public final String getLineSeparator()
3120 {
3121 if ( this.lineSeparator == null )
3122 {
3123 this.lineSeparator = System.getProperty( "line.separator", "\n" );
3124
3125 if ( this.isLoggable( Level.CONFIG ) )
3126 {
3127 this.log( Level.CONFIG, getMessage( "defaultLineSeparator",
3128 StringEscapeUtils.escapeJava( this.lineSeparator ) ), null );
3129
3130 }
3131 }
3132
3133 return this.lineSeparator;
3134 }
3135
3136 /**
3137 * Sets the line separator of the instance.
3138 *
3139 * @param value The new line separator of the instance or {@code null}.
3140 *
3141 * @see #getLineSeparator()
3142 */
3143 public final void setLineSeparator( final String value )
3144 {
3145 this.lineSeparator = value;
3146 }
3147
3148 /**
3149 * Gets the locale of the instance.
3150 *
3151 * @return The locale of the instance.
3152 *
3153 * @see #setLocale(java.util.Locale)
3154 *
3155 * @since 1.2
3156 */
3157 public final Locale getLocale()
3158 {
3159 if ( this.locale == null )
3160 {
3161 this.locale = Locale.ENGLISH;
3162
3163 if ( this.isLoggable( Level.CONFIG ) )
3164 {
3165 this.log( Level.CONFIG, getMessage( "defaultLocale", this.locale ), null );
3166 }
3167 }
3168
3169 return this.locale;
3170 }
3171
3172 /**
3173 * Sets the locale of the instance.
3174 *
3175 * @param value The new locale of the instance or {@code null}.
3176 *
3177 * @see #getLocale()
3178 *
3179 * @since 1.2
3180 */
3181 public final void setLocale( final Locale value )
3182 {
3183 this.locale = value;
3184 }
3185
3186 /**
3187 * Gets an {@code ExecutorService} used to run tasks in parallel.
3188 *
3189 * @return An {@code ExecutorService} used to run tasks in parallel or {@code null}, if no such service has been
3190 * provided by an application.
3191 *
3192 * @since 1.10
3193 *
3194 * @see #setExecutorService(java.util.concurrent.ExecutorService)
3195 */
3196 public final ExecutorService getExecutorService()
3197 {
3198 return this.executorService;
3199 }
3200
3201 /**
3202 * Sets the {@code ExecutorService} to be used to run tasks in parallel.
3203 * <p>
3204 * The {@code ExecutorService} to be used to run tasks in parallel is an optional entity. If no such service is
3205 * provided by an application, no parallelization is performed. Configuration or lifecycle management of the given
3206 * {@code ExecutorService} is the responsibility of the application.
3207 * </p>
3208 *
3209 * @param value The {@code ExecutorService} to be used to run tasks in parallel or {@code null}, to disable any
3210 * parallelization.
3211 *
3212 * @since 1.10
3213 *
3214 * @see #getExecutorService()
3215 */
3216 public final void setExecutorService( final ExecutorService value )
3217 {
3218 this.executorService = value;
3219 if ( this.executorService != null )
3220 {
3221 this.initDefaults();
3222 }
3223 }
3224
3225 /**
3226 * Gets a velocity template for a given name.
3227 * <p>
3228 * This method searches templates at the following locations recursively in the shown order stopping whenever
3229 * a matching template is found.
3230 * <ol>
3231 * <li><code>org/jomc/tools/templates/{@link #getTemplateProfile() profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
3232 * <li><code>org/jomc/tools/templates/{@link #getParentTemplateProfile(java.lang.String) parent profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
3233 * <li><code>org/jomc/tools/templates/{@link #getTemplateProfile() profile}/<i>templateName</i></code></li>
3234 * <li><code>org/jomc/tools/templates/{@link #getParentTemplateProfile(java.lang.String) parent profile}/{@link #getLocale() language}/<i>templateName</i></code></li>
3235 * </ol></p>
3236 *
3237 * @param templateName The name of the template to get.
3238 *
3239 * @return The template matching {@code templateName}.
3240 *
3241 * @throws NullPointerException if {@code templateName} is {@code null}.
3242 * @throws FileNotFoundException if no such template is found.
3243 * @throws IOException if getting the template fails.
3244 *
3245 * @see #getTemplateProfile()
3246 * @see #getParentTemplateProfile(java.lang.String)
3247 * @see #getLocale()
3248 * @see #getTemplateEncoding(java.lang.String)
3249 * @see #getVelocityEngine()
3250 */
3251 public Template getVelocityTemplate( final String templateName ) throws FileNotFoundException, IOException
3252 {
3253 if ( templateName == null )
3254 {
3255 throw new NullPointerException( "templateName" );
3256 }
3257
3258 return this.getVelocityTemplate( this.getTemplateProfile(), templateName );
3259 }
3260
3261 /**
3262 * Notifies registered listeners.
3263 *
3264 * @param level The level of the event.
3265 * @param message The message of the event or {@code null}.
3266 * @param throwable The throwable of the event or {@code null}.
3267 *
3268 * @throws NullPointerException if {@code level} is {@code null}.
3269 *
3270 * @see #getListeners()
3271 * @see #isLoggable(java.util.logging.Level)
3272 */
3273 public void log( final Level level, final String message, final Throwable throwable )
3274 {
3275 if ( level == null )
3276 {
3277 throw new NullPointerException( "level" );
3278 }
3279
3280 if ( this.isLoggable( level ) )
3281 {
3282 for ( final Listener listener : this.getListeners() )
3283 {
3284 listener.onLog( level, message, throwable );
3285 }
3286 }
3287 }
3288
3289 void initDefaults()
3290 {
3291 this.getLogLevel();
3292 this.getModel();
3293 this.getVelocityEngine();
3294 this.getDefaultTemplateEncoding();
3295 this.getInputEncoding();
3296 this.getOutputEncoding();
3297 this.getTemplateProfile();
3298 this.getIndentation();
3299 this.getLineSeparator();
3300 this.getLocale();
3301 }
3302
3303 private Template findVelocityTemplate( final String location, final String encoding ) throws IOException
3304 {
3305 try
3306 {
3307 return this.getVelocityEngine().getTemplate( location, encoding );
3308 }
3309 catch ( final ResourceNotFoundException e )
3310 {
3311 if ( this.isLoggable( Level.FINER ) )
3312 {
3313 this.log( Level.FINER, getMessage( "templateNotFound", location ), null );
3314 }
3315
3316 return null;
3317 }
3318 catch ( final ParseErrorException e )
3319 {
3320 String m = getMessage( e );
3321 m = m == null ? "" : " " + m;
3322
3323 // JDK: As of JDK 6, "new IOException( message, cause )".
3324 throw (IOException) new IOException( getMessage( "invalidTemplate", location, m ) ).initCause( e );
3325 }
3326 catch ( final VelocityException e )
3327 {
3328 String m = getMessage( e );
3329 m = m == null ? "" : " " + m;
3330
3331 // JDK: As of JDK 6, "new IOException( message, cause )".
3332 throw (IOException) new IOException( getMessage( "velocityException", location, m ) ).initCause( e );
3333 }
3334 }
3335
3336 private java.util.Properties getTemplateProfileContextProperties( final String profileName, final String language )
3337 throws IOException
3338 {
3339 Map<String, java.util.Properties> map = this.templateProfileContextPropertiesCache == null
3340 ? null : this.templateProfileContextPropertiesCache.get();
3341
3342 if ( map == null )
3343 {
3344 map = new ConcurrentHashMap<String, java.util.Properties>();
3345 this.templateProfileContextPropertiesCache = new SoftReference<Map<String, java.util.Properties>>( map );
3346 }
3347
3348 final String key = profileName + "|" + language;
3349 java.util.Properties profileProperties = map.get( key );
3350
3351 if ( profileProperties == null )
3352 {
3353 InputStream in = null;
3354 URL url = null;
3355 profileProperties = new java.util.Properties();
3356
3357 final String resourceName = TEMPLATE_PREFIX + profileName + ( language == null ? "" : "/" + language )
3358 + "/context.properties";
3359
3360 try
3361 {
3362 url = this.getClass().getResource( "/" + resourceName );
3363
3364 if ( url != null )
3365 {
3366 in = url.openStream();
3367
3368 if ( this.isLoggable( Level.CONFIG ) )
3369 {
3370 this.log( Level.CONFIG, getMessage( "contextPropertiesFound", url.toExternalForm() ), null );
3371 }
3372
3373 profileProperties.load( in );
3374
3375 in.close();
3376 in = null;
3377 }
3378 else if ( this.getTemplateLocation() != null )
3379 {
3380 if ( this.isLoggable( Level.CONFIG ) )
3381 {
3382 this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", resourceName ), null );
3383 }
3384
3385 url = new URL( this.getTemplateLocation(), resourceName );
3386 in = url.openStream();
3387
3388 if ( this.isLoggable( Level.CONFIG ) )
3389 {
3390 this.log( Level.CONFIG, getMessage( "contextPropertiesFound", url.toExternalForm() ), null );
3391 }
3392
3393 profileProperties.load( in );
3394
3395 in.close();
3396 in = null;
3397 }
3398 else if ( this.isLoggable( Level.CONFIG ) )
3399 {
3400 this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", resourceName ), null );
3401 }
3402 }
3403 catch ( final FileNotFoundException e )
3404 {
3405 if ( this.isLoggable( Level.CONFIG ) )
3406 {
3407 this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", url.toExternalForm() ), null );
3408 }
3409 }
3410 finally
3411 {
3412 map.put( key, profileProperties );
3413
3414 try
3415 {
3416 if ( in != null )
3417 {
3418 in.close();
3419 }
3420 }
3421 catch ( final IOException e )
3422 {
3423 this.log( Level.SEVERE, getMessage( e ), e );
3424 }
3425 }
3426 }
3427
3428 return profileProperties;
3429 }
3430
3431 private void mergeTemplateProfileContextProperties( final String profileName, final String language,
3432 final VelocityContext velocityContext ) throws IOException
3433 {
3434 if ( profileName != null )
3435 {
3436 final java.util.Properties templateProfileProperties =
3437 this.getTemplateProfileContextProperties( profileName, language );
3438
3439 for ( final Enumeration<?> e = templateProfileProperties.propertyNames(); e.hasMoreElements(); )
3440 {
3441 final String name = e.nextElement().toString();
3442 final String value = templateProfileProperties.getProperty( name );
3443 final String[] values = value.split( "\\|" );
3444
3445 if ( !velocityContext.containsKey( name ) )
3446 {
3447 final String className = values[0];
3448
3449 try
3450 {
3451 if ( values.length > 1 )
3452 {
3453 final Class<?> valueClass = Class.forName( className );
3454 velocityContext.put( name,
3455 valueClass.getConstructor( String.class ).newInstance( values[1] ) );
3456 }
3457 else if ( value.contains( "|" ) )
3458 {
3459 velocityContext.put( name, Class.forName( values[0] ).newInstance() );
3460 }
3461 else
3462 {
3463 velocityContext.put( name, value );
3464 }
3465 }
3466 catch ( final InstantiationException ex )
3467 {
3468 // JDK: As of JDK 6, "new IOException( message, cause )".
3469 throw (IOException) new IOException( getMessage(
3470 "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
3471 initCause( ex );
3472
3473 }
3474 catch ( final IllegalAccessException ex )
3475 {
3476 // JDK: As of JDK 6, "new IOException( message, cause )".
3477 throw (IOException) new IOException( getMessage(
3478 "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
3479 initCause( ex );
3480
3481 }
3482 catch ( final InvocationTargetException ex )
3483 {
3484 // JDK: As of JDK 6, "new IOException( message, cause )".
3485 throw (IOException) new IOException( getMessage(
3486 "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
3487 initCause( ex );
3488
3489 }
3490 catch ( final NoSuchMethodException ex )
3491 {
3492 // JDK: As of JDK 6, "new IOException( message, cause )".
3493 throw (IOException) new IOException( getMessage(
3494 "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
3495 initCause( ex );
3496
3497 }
3498 catch ( final ClassNotFoundException ex )
3499 {
3500 // JDK: As of JDK 6, "new IOException( message, cause )".
3501 throw (IOException) new IOException( getMessage(
3502 "contextPropertiesException", profileName + ( language != null ? ", " + language : "" ) ) ).
3503 initCause( ex );
3504
3505 }
3506 }
3507 }
3508
3509 this.mergeTemplateProfileContextProperties( this.getParentTemplateProfile( profileName ), language,
3510 velocityContext );
3511
3512 }
3513 }
3514
3515 private java.util.Properties getTemplateProfileProperties( final String profileName ) throws IOException
3516 {
3517 Map<String, java.util.Properties> map = this.templateProfilePropertiesCache == null
3518 ? null : this.templateProfilePropertiesCache.get();
3519
3520 if ( map == null )
3521 {
3522 map = new ConcurrentHashMap<String, java.util.Properties>();
3523 this.templateProfilePropertiesCache = new SoftReference<Map<String, java.util.Properties>>( map );
3524 }
3525
3526 java.util.Properties profileProperties = map.get( profileName );
3527
3528 if ( profileProperties == null )
3529 {
3530 InputStream in = null;
3531 profileProperties = new java.util.Properties();
3532
3533 final String resourceName = TEMPLATE_PREFIX + profileName + "/profile.properties";
3534 URL url = null;
3535
3536 try
3537 {
3538 url = this.getClass().getResource( "/" + resourceName );
3539
3540 if ( url != null )
3541 {
3542 in = url.openStream();
3543
3544 if ( this.isLoggable( Level.CONFIG ) )
3545 {
3546 this.log( Level.CONFIG, getMessage( "templateProfilePropertiesFound", url.toExternalForm() ),
3547 null );
3548
3549 }
3550
3551 profileProperties.load( in );
3552
3553 in.close();
3554 in = null;
3555 }
3556 else if ( this.getTemplateLocation() != null )
3557 {
3558 if ( this.isLoggable( Level.CONFIG ) )
3559 {
3560 this.log( Level.CONFIG, getMessage( "templateProfilePropertiesNotFound", resourceName ), null );
3561 }
3562
3563 url = new URL( this.getTemplateLocation(), resourceName );
3564 in = url.openStream();
3565
3566 if ( this.isLoggable( Level.CONFIG ) )
3567 {
3568 this.log( Level.CONFIG, getMessage( "templateProfilePropertiesFound", url.toExternalForm() ),
3569 null );
3570
3571 }
3572
3573 profileProperties.load( in );
3574
3575 in.close();
3576 in = null;
3577 }
3578 else if ( this.isLoggable( Level.CONFIG ) )
3579 {
3580 this.log( Level.CONFIG, getMessage( "templateProfilePropertiesNotFound", resourceName ), null );
3581 }
3582 }
3583 catch ( final FileNotFoundException e )
3584 {
3585 if ( this.isLoggable( Level.CONFIG ) )
3586 {
3587 this.log( Level.CONFIG, getMessage( "templateProfilePropertiesNotFound", url.toExternalForm() ),
3588 null );
3589
3590 }
3591 }
3592 finally
3593 {
3594 map.put( profileName, profileProperties );
3595
3596 try
3597 {
3598 if ( in != null )
3599 {
3600 in.close();
3601 }
3602 }
3603 catch ( final IOException e )
3604 {
3605 this.log( Level.SEVERE, getMessage( e ), e );
3606 }
3607 }
3608 }
3609
3610 return profileProperties;
3611 }
3612
3613 private Set<String> getJavaKeywords()
3614 {
3615 BufferedReader in = null;
3616 Set<String> set = this.javaKeywordsCache == null ? null : this.javaKeywordsCache.get();
3617
3618 try
3619 {
3620 if ( set == null )
3621 {
3622 in = new BufferedReader( new InputStreamReader( this.getClass().getResourceAsStream(
3623 "/" + this.getClass().getPackage().getName().replace( ".", "/" ) + "/JavaKeywords.txt" ),
3624 "UTF-8" ) );
3625
3626 final Set<String> keywords = new HashSet<String>();
3627
3628 for ( String line = in.readLine(); line != null; line = in.readLine() )
3629 {
3630 keywords.add( line );
3631 }
3632
3633 in.close();
3634 in = null;
3635
3636 set = Collections.unmodifiableSet( keywords );
3637 this.javaKeywordsCache = new SoftReference<Set<String>>( set );
3638 }
3639 }
3640 catch ( final IOException e )
3641 {
3642 throw new IllegalStateException( getMessage( e ), e );
3643 }
3644 finally
3645 {
3646 try
3647 {
3648 if ( in != null )
3649 {
3650 in.close();
3651 }
3652 }
3653 catch ( final IOException e )
3654 {
3655 throw new IllegalStateException( getMessage( e ), e );
3656 }
3657 }
3658
3659 return set;
3660 }
3661
3662 private Template getVelocityTemplate( final String tp, final String tn ) throws IOException
3663 {
3664 Template template = null;
3665
3666 if ( tp != null )
3667 {
3668 final String key = this.getLocale() + "|" + this.getTemplateProfile() + "|"
3669 + this.getDefaultTemplateProfile() + "|" + tn;
3670
3671 Map<String, TemplateData> map = this.templateCache == null
3672 ? null : this.templateCache.get();
3673
3674 if ( map == null )
3675 {
3676 map = new ConcurrentHashMap<String, TemplateData>( 32 );
3677 this.templateCache = new SoftReference<Map<String, TemplateData>>( map );
3678 }
3679
3680 TemplateData templateData = map.get( key );
3681
3682 if ( templateData == null )
3683 {
3684 templateData = new TemplateData();
3685
3686 if ( !StringUtils.EMPTY.equals( this.getLocale().getLanguage() ) )
3687 {
3688 templateData.location = TEMPLATE_PREFIX + tp + "/" + this.getLocale().getLanguage() + "/" + tn;
3689 templateData.template =
3690 this.findVelocityTemplate( templateData.location, this.getTemplateEncoding( tp ) );
3691
3692 }
3693
3694 if ( templateData.template == null )
3695 {
3696 templateData.location = TEMPLATE_PREFIX + tp + "/" + tn;
3697 templateData.template =
3698 this.findVelocityTemplate( templateData.location, this.getTemplateEncoding( tp ) );
3699
3700 }
3701
3702 if ( templateData.template == null )
3703 {
3704 template = this.getVelocityTemplate( this.getParentTemplateProfile( tp ), tn );
3705
3706 if ( template == null )
3707 {
3708 map.put( key, new TemplateData() );
3709 throw new FileNotFoundException( getMessage( "noSuchTemplate", tn ) );
3710 }
3711 }
3712 else
3713 {
3714 if ( this.isLoggable( Level.FINER ) )
3715 {
3716 this.log( Level.FINER, getMessage( "templateInfo", tn, templateData.location ), null );
3717 }
3718
3719 template = templateData.template;
3720 map.put( key, templateData );
3721 }
3722 }
3723 else if ( templateData.template == null )
3724 {
3725 throw new FileNotFoundException( getMessage( "noSuchTemplate", tn ) );
3726 }
3727 else
3728 {
3729 if ( this.isLoggable( Level.FINER ) )
3730 {
3731 this.log( Level.FINER, getMessage( "templateInfo", tn, templateData.location ), null );
3732 }
3733
3734 template = templateData.template;
3735 }
3736 }
3737
3738 return template;
3739 }
3740
3741 private static String getMessage( final String key, final Object... arguments )
3742 {
3743 return MessageFormat.format( ResourceBundle.getBundle(
3744 JomcTool.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
3745
3746 }
3747
3748 private static String getMessage( final Throwable t )
3749 {
3750 return t != null
3751 ? t.getMessage() != null && t.getMessage().trim().length() > 0
3752 ? t.getMessage()
3753 : getMessage( t.getCause() )
3754 : null;
3755
3756 }
3757
3758 /**
3759 * @since 1.3
3760 */
3761 private static class TemplateData
3762 {
3763
3764 private volatile String location;
3765
3766 private volatile Template template;
3767
3768 }
3769
3770 }