001 /*
002 * Copyright (c) 2009 The JOMC Project
003 * Copyright (c) 2005 Christian Schulte <cs@jomc.org>
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions
008 * are met:
009 *
010 * o Redistributions of source code must retain the above copyright
011 * notice, this list of conditions and the following disclaimer.
012 *
013 * o Redistributions in binary form must reproduce the above copyright
014 * notice, this list of conditions and the following disclaimer in
015 * the documentation and/or other materials provided with the
016 * distribution.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
022 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
027 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
028 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 *
030 * $Id: JomcTool.java 739 2009-10-06 01:54:08Z schulte2005 $
031 *
032 */
033 package org.jomc.tools;
034
035 import java.io.ByteArrayInputStream;
036 import java.io.ByteArrayOutputStream;
037 import java.io.IOException;
038 import java.io.InputStreamReader;
039 import java.io.OutputStreamWriter;
040 import java.text.DateFormat;
041 import java.text.Format;
042 import java.text.MessageFormat;
043 import java.text.SimpleDateFormat;
044 import java.util.ArrayList;
045 import java.util.Calendar;
046 import java.util.Date;
047 import java.util.LinkedList;
048 import java.util.List;
049 import java.util.Locale;
050 import java.util.ResourceBundle;
051 import java.util.logging.Level;
052 import org.apache.commons.lang.StringEscapeUtils;
053 import org.apache.velocity.Template;
054 import org.apache.velocity.VelocityContext;
055 import org.apache.velocity.app.VelocityEngine;
056 import org.apache.velocity.exception.ResourceNotFoundException;
057 import org.apache.velocity.runtime.RuntimeConstants;
058 import org.apache.velocity.runtime.RuntimeServices;
059 import org.apache.velocity.runtime.log.LogChute;
060 import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
061 import org.jomc.model.Argument;
062 import org.jomc.model.ArgumentType;
063 import org.jomc.model.DefaultModelManager;
064 import org.jomc.model.Dependency;
065 import org.jomc.model.Implementation;
066 import org.jomc.model.Message;
067 import org.jomc.model.ModelManager;
068 import org.jomc.model.Modules;
069 import org.jomc.model.Multiplicity;
070 import org.jomc.model.Properties;
071 import org.jomc.model.Property;
072 import org.jomc.model.Specification;
073 import org.jomc.model.SpecificationReference;
074 import org.jomc.model.Specifications;
075 import org.jomc.model.Text;
076
077 /**
078 * Base tool class.
079 *
080 * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
081 * @version $Id: JomcTool.java 739 2009-10-06 01:54:08Z schulte2005 $
082 */
083 public abstract class JomcTool
084 {
085
086 /** Listener interface. */
087 public interface Listener
088 {
089
090 /**
091 * Get called on logging.
092 *
093 * @param level The level of the event.
094 * @param message The message of the event or {@code null}.
095 * @param throwable The throwable of the event or {@code null}.
096 */
097 void onLog( Level level, String message, Throwable throwable );
098
099 }
100
101 /** Empty byte array. */
102 private static final byte[] NO_BYTES =
103 {
104 };
105
106 /** The prefix of the template location. */
107 private static final String TEMPLATE_PREFIX =
108 JomcTool.class.getPackage().getName().replace( '.', '/' ) + "/templates/";
109
110 /** Name of the velocity classpath resource loader implementation. */
111 private static final String VELOCITY_RESOURCE_LOADER = ClasspathResourceLoader.class.getName();
112
113 /** The modules of the instance. */
114 private Modules modules;
115
116 /** The model manager of the instance. */
117 private ModelManager modelManager;
118
119 /** {@code VelocityEngine} of the generator. */
120 private VelocityEngine velocityEngine;
121
122 /** The encoding to use for reading templates. */
123 private String templateEncoding;
124
125 /** The encoding to use for reading files. */
126 private String inputEncoding;
127
128 /** The encoding to use for writing files. */
129 private String outputEncoding;
130
131 /** The profile of the instance. */
132 private String profile;
133
134 /** The listeners of the instance. */
135 private List<Listener> listeners;
136
137 /** Creates a new {@code JomcTool} instance. */
138 public JomcTool()
139 {
140 super();
141 }
142
143 /**
144 * Creates a new {@code JomcTool} instance taking a {@code JomcTool} instance to initialize the new instance with.
145 *
146 * @param tool The instance to initialize the new instance with.
147 */
148 public JomcTool( final JomcTool tool )
149 {
150 this();
151 if ( tool != null )
152 {
153 try
154 {
155 this.setTemplateEncoding( tool.getTemplateEncoding() );
156 this.setInputEncoding( tool.getInputEncoding() );
157 this.setOutputEncoding( tool.getOutputEncoding() );
158 this.setModelManager( tool.getModelManager() );
159 this.setModules( tool.getModules() );
160 this.setProfile( tool.getProfile() );
161 this.setVelocityEngine( tool.getVelocityEngine() );
162 this.getListeners().addAll( tool.getListeners() );
163 }
164 catch ( final Exception e )
165 {
166 this.log( Level.SEVERE, e.getMessage(), e );
167 }
168 }
169 }
170
171 /**
172 * Gets the list of registered listeners.
173 *
174 * @return The list of registered listeners.
175 */
176 public List<Listener> getListeners()
177 {
178 if ( this.listeners == null )
179 {
180 this.listeners = new LinkedList<Listener>();
181 }
182
183 return this.listeners;
184 }
185
186 /**
187 * Gets the Java package name of a specification.
188 *
189 * @param specification The specification to get the Java package name of.
190 *
191 * @return The Java package name of {@code specification}.
192 *
193 * @throws NullPointerException if {@code specification} is {@code null}.
194 */
195 public String getJavaPackageName( final Specification specification )
196 {
197 if ( specification == null )
198 {
199 throw new NullPointerException( "specification" );
200 }
201
202 return this.getJavaPackageName( specification.getClazz() );
203 }
204
205 /**
206 * Gets the Java type name of a specification.
207 *
208 * @param specification The specification to get the Java type name of.
209 * @param qualified {@code true} to return the fully qualified type name (with package name prepended);
210 * {@code false} to return the short type name (without package name prepended).
211 *
212 * @return The Java type name of {@code specification}.
213 *
214 * @throws NullPointerException if {@code specification} is {@code null}.
215 */
216 public String getJavaTypeName( final Specification specification, final boolean qualified )
217 {
218 if ( specification == null )
219 {
220 throw new NullPointerException( "specification" );
221 }
222
223 final StringBuilder typeName = new StringBuilder();
224 final String javaPackageName = this.getJavaPackageName( specification );
225
226 if ( qualified && javaPackageName.length() > 0 )
227 {
228 typeName.append( javaPackageName ).append( '.' );
229 }
230
231 typeName.append( javaPackageName.length() > 0
232 ? specification.getClazz().substring( javaPackageName.length() + 1 )
233 : specification.getClazz() );
234
235 return typeName.toString();
236 }
237
238 /**
239 * Gets the Java class path location of a specification.
240 *
241 * @param specification The specification to return the Java class path location of.
242 *
243 * @return the Java class path location of {@code specification}.
244 *
245 * @throws NullPointerException if {@code specification} is {@code null}.
246 */
247 public String getJavaClasspathLocation( final Specification specification )
248 {
249 if ( specification == null )
250 {
251 throw new NullPointerException( "specification" );
252 }
253
254 return ( this.getJavaTypeName( specification, true ) ).replace( '.', '/' );
255 }
256
257 /**
258 * Gets the Java package name of a specification reference.
259 *
260 * @param reference The specification reference to get the Java package name of.
261 *
262 * @return The Java package name of {@code reference}.
263 *
264 * @throws NullPointerException if {@code reference} is {@code null}.
265 */
266 public String getJavaPackageName( final SpecificationReference reference )
267 {
268 if ( reference == null )
269 {
270 throw new NullPointerException( "reference" );
271 }
272
273 final Specification s = this.getModules().getSpecification( reference.getIdentifier() );
274 assert s != null : "Specification '" + reference.getIdentifier() + "' not found.";
275 return this.getJavaPackageName( s );
276 }
277
278 /**
279 * Gets the name of a Java type of a given specification reference.
280 *
281 * @param reference The specification reference to get a Java type name of.
282 * @param qualified {@code true} to return the fully qualified type name (with package name prepended);
283 * {@code false} to return the short type name (without package name prepended).
284 *
285 * @return The Java type name of {@code reference}.
286 *
287 * @throws NullPointerException if {@code reference} is {@code null}.
288 */
289 public String getJavaTypeName( final SpecificationReference reference, final boolean qualified )
290 {
291 if ( reference == null )
292 {
293 throw new NullPointerException( "reference" );
294 }
295
296 final Specification s = this.getModules().getSpecification( reference.getIdentifier() );
297 assert s != null : "Specification '" + reference.getIdentifier() + "' not found.";
298 return this.getJavaTypeName( s, qualified );
299 }
300
301 /**
302 * Gets the Java package name of an implementation.
303 *
304 * @param implementation The implementation to get the Java package name of.
305 *
306 * @return The Java package name of {@code implementation}.
307 *
308 * @throws NullPointerException if {@code implementation} is {@code null}.
309 */
310 public String getJavaPackageName( final Implementation implementation )
311 {
312 if ( implementation == null )
313 {
314 throw new NullPointerException( "implementation" );
315 }
316
317 return this.getJavaPackageName( implementation.getClazz() );
318 }
319
320 /**
321 * Gets the Java type name of an implementation.
322 *
323 * @param implementation The implementation to get the Java type name of.
324 * @param qualified {@code true} to return the fully qualified type name (with package name prepended);
325 * {@code false} to return the short type name (without package name prepended).
326 *
327 * @return The Java type name of {@code implementation}.
328 *
329 * @throws NullPointerException if {@code implementation} is {@code null}.
330 */
331 public String getJavaTypeName( final Implementation implementation, final boolean qualified )
332 {
333 if ( implementation == null )
334 {
335 throw new NullPointerException( "implementation" );
336 }
337
338 final StringBuilder typeName = new StringBuilder();
339 final String javaPackageName = this.getJavaPackageName( implementation );
340
341 if ( qualified && javaPackageName.length() > 0 )
342 {
343 typeName.append( javaPackageName ).append( '.' );
344 }
345
346 typeName.append( javaPackageName.length() > 0
347 ? implementation.getClazz().substring( javaPackageName.length() + 1 )
348 : implementation.getClazz() );
349
350 return typeName.toString();
351 }
352
353 /**
354 * Gets the Java class path location of an implementation.
355 *
356 * @param implementation The implementation to return the Java class path location of.
357 *
358 * @return The Java class path location of {@code implementation}.
359 *
360 * @throws NullPointerException if {@code implementation} is {@code null}.
361 */
362 public String getJavaClasspathLocation( final Implementation implementation )
363 {
364 if ( implementation == null )
365 {
366 throw new NullPointerException( "implementation" );
367 }
368
369 return ( this.getJavaTypeName( implementation, true ) ).replace( '.', '/' );
370 }
371
372 /**
373 * Gets all Java interfaces an implementation implements.
374 *
375 * @param implementation The implementation to get all implemented Java interfaces of.
376 * @param qualified {@code true} to return the fully qualified type names (with package name prepended);
377 * {@code false} to return the short type names (without package name prepended).
378 *
379 * @return All interfaces implemented by {@code implementation}.
380 *
381 * @throws NullPointerException if {@code implementation} is {@code null}.
382 */
383 public List<String> getJavaInterfaces( final Implementation implementation, final boolean qualified )
384 {
385 if ( implementation == null )
386 {
387 throw new NullPointerException( "implementation" );
388 }
389
390 final Specifications specs = this.getModules().getSpecifications( implementation.getIdentifier() );
391 final List<String> col = new ArrayList<String>( specs == null ? 0 : specs.getSpecification().size() );
392
393 if ( specs != null )
394 {
395 for ( Specification s : specs.getSpecification() )
396 {
397 final String typeName = this.getJavaTypeName( s, qualified );
398 if ( !col.contains( typeName ) )
399 {
400 col.add( typeName );
401 }
402 }
403 }
404
405 return col;
406 }
407
408 /**
409 * Gets the Java type name of an argument.
410 *
411 * @param argument The argument to get the Java type name of.
412 *
413 * @return The Java type name of {@code argument}.
414 *
415 * @throws NullPointerException if {@code argument} is {@code null}.
416 */
417 public String getJavaTypeName( final Argument argument )
418 {
419 if ( argument == null )
420 {
421 throw new NullPointerException( "argument" );
422 }
423
424 if ( argument.getType() == ArgumentType.DATE || argument.getType() == ArgumentType.TIME )
425 {
426 return "java.util.Date";
427 }
428 else if ( argument.getType() == ArgumentType.NUMBER )
429 {
430 return "java.lang.Number";
431 }
432 else if ( argument.getType() == ArgumentType.TEXT )
433 {
434 return "java.lang.String";
435 }
436 else
437 {
438 throw new IllegalArgumentException( argument.getType().value() );
439 }
440 }
441
442 /**
443 * Gets the Java type name of a property.
444 *
445 * @param property The property to get the Java type name of.
446 * @param boxify {@code true} to return the name of the Java wrapper class when the type is a Java primitive type;
447 * {@code false} to return the exact binary name (unboxed name) of the Java type.
448 *
449 * @return The Java type name of {@code property}.
450 *
451 * @throws NullPointerException if {@code property} is {@code null}.
452 */
453 public String getJavaTypeName( final Property property, final boolean boxify )
454 {
455 if ( property == null )
456 {
457 throw new NullPointerException( "property" );
458 }
459
460 if ( property.getAny() != null )
461 {
462 return Object.class.getName();
463 }
464 if ( property.getType() != null )
465 {
466 final String typeName = property.getType();
467
468 if ( boxify )
469 {
470 if ( Boolean.TYPE.getName().equals( typeName ) )
471 {
472 return Boolean.class.getName();
473 }
474 if ( Byte.TYPE.getName().equals( typeName ) )
475 {
476 return Byte.class.getName();
477 }
478 if ( Character.TYPE.getName().equals( typeName ) )
479 {
480 return Character.class.getName();
481 }
482 if ( Double.TYPE.getName().equals( typeName ) )
483 {
484 return Double.class.getName();
485 }
486 if ( Float.TYPE.getName().equals( typeName ) )
487 {
488 return Float.class.getName();
489 }
490 if ( Integer.TYPE.getName().equals( typeName ) )
491 {
492 return Integer.class.getName();
493 }
494 if ( Long.TYPE.getName().equals( typeName ) )
495 {
496 return Long.class.getName();
497 }
498 if ( Short.TYPE.getName().equals( typeName ) )
499 {
500 return Short.class.getName();
501 }
502 }
503
504 return typeName;
505 }
506
507 return String.class.getName();
508 }
509
510 /**
511 * Gets a flag indicating if the type of a given property is a Java primitive.
512 *
513 * @param property The property to query.
514 *
515 * @return {@code true} if the type of {@code property} is a Java primitive; {@code false} if not.
516 *
517 * @throws NullPointerException if {@code property} is {@code null}.
518 */
519 public boolean isJavaPrimitiveType( final Property property )
520 {
521 if ( property == null )
522 {
523 throw new NullPointerException( "property" );
524 }
525
526 return !this.getJavaTypeName( property, false ).equals( this.getJavaTypeName( property, true ) );
527 }
528
529 /**
530 * Gets the name of a Java accessor method of a given property.
531 *
532 * @param property The property to get a Java accessor method name of.
533 *
534 * @return The Java accessor method name of {@code property}.
535 *
536 * @throws NullPointerException if {@code property} is {@code null}.
537 */
538 public String getJavaGetterMethodName( final Property property )
539 {
540 if ( property == null )
541 {
542 throw new NullPointerException( "property" );
543 }
544
545 final char[] name = property.getName().toCharArray();
546 name[0] = Character.toUpperCase( name[0] );
547 String prefix = "get";
548
549 final String javaTypeName = this.getJavaTypeName( property, true );
550 if ( Boolean.class.getName().equals( javaTypeName ) )
551 {
552 prefix = "is";
553 }
554
555 return prefix + String.valueOf( name );
556 }
557
558 /**
559 * Gets the name of a Java type of a given dependency.
560 *
561 * @param dependency The dependency to get a dependency Java type name of.
562 *
563 * @return The Java type name of {@code dependency}.
564 *
565 * @throws NullPointerException if {@code dependency} is {@code null}.
566 */
567 public String getJavaTypeName( final Dependency dependency )
568 {
569 if ( dependency == null )
570 {
571 throw new NullPointerException( "dependency" );
572 }
573
574 final StringBuilder typeName = new StringBuilder();
575 typeName.append( this.getJavaTypeName( (SpecificationReference) dependency, true ) );
576
577 final Specification s = this.getModules().getSpecification( dependency.getIdentifier() );
578 if ( s != null && s.getMultiplicity() == Multiplicity.MANY && dependency.getImplementationName() == null )
579 {
580 typeName.append( "[]" );
581 }
582
583 return typeName.toString();
584 }
585
586 /**
587 * Gets the name of a Java accessor method of a given dependency.
588 *
589 * @param dependency The dependency to get a Java accessor method name of.
590 *
591 * @return The Java accessor method name of {@code dependency}.
592 *
593 * @throws NullPointerException if {@code dependency} is {@code null}.
594 */
595 public String getJavaGetterMethodName( final Dependency dependency )
596 {
597 if ( dependency == null )
598 {
599 throw new NullPointerException( "dependency" );
600 }
601
602 final char[] name = dependency.getName().toCharArray();
603 name[0] = Character.toUpperCase( name[0] );
604 return "get" + String.valueOf( name );
605 }
606
607 /**
608 * Gets the name of a Java accessor method of a given message.
609 *
610 * @param message The message to get a Java accessor method name of.
611 *
612 * @return The Java accessor method name of {@code message}.
613 *
614 * @throws NullPointerException if {@code message} is {@code null}.
615 */
616 public String getJavaGetterMethodName( final Message message )
617 {
618 if ( message == null )
619 {
620 throw new NullPointerException( "message" );
621 }
622
623 final char[] name = message.getName().toCharArray();
624 name[0] = Character.toUpperCase( name[0] );
625 return "get" + String.valueOf( name ) + "Message";
626 }
627
628 /**
629 * Gets the name of a Java modifier of a dependency of a given implementation.
630 *
631 * @param implementation The implementation to get a dependency Java modifier name of.
632 * @param dependency The dependency to get a Java modifier name of.
633 *
634 * @return The Java modifier name of {@code dependency} of {@code implementation}.
635 *
636 * @throws NullPointerException if {@code implementation} or {@code dependency} is {@code null}.
637 */
638 public String getJavaModifierName( final Implementation implementation, final Dependency dependency )
639 {
640 if ( implementation == null )
641 {
642 throw new NullPointerException( "implementation" );
643 }
644 if ( dependency == null )
645 {
646 throw new NullPointerException( "dependency" );
647 }
648
649 return "private";
650 }
651
652 /**
653 * Gets the name of a Java modifier of a message of a given implementation.
654 *
655 * @param implementation The implementation to get a message Java modifier name of.
656 * @param message The message to get a Java modifier name of.
657 *
658 * @return The Java modifier name of {@code message} of {@code implementation}.
659 *
660 * @throws NullPointerException if {@code implementation} or {@code message} is {@code null}.
661 */
662 public String getJavaModifierName( final Implementation implementation, final Message message )
663 {
664 if ( implementation == null )
665 {
666 throw new NullPointerException( "implementation" );
667 }
668 if ( message == null )
669 {
670 throw new NullPointerException( "message" );
671 }
672
673 return "private";
674 }
675
676 /**
677 * Gets the name of a Java modifier for a given property of a given implementation.
678 *
679 * @param implementation The implementation declaring {@code property}.
680 * @param property The property to get a Java modifier name for.
681 *
682 * @return The Java modifier name for {@code property} of {@code implementation}.
683 *
684 * @throws NullPointerException if {@code implementation} or {@code property} is {@code null}.
685 */
686 public String getJavaModifierName( final Implementation implementation, final Property property )
687 {
688 if ( implementation == null )
689 {
690 throw new NullPointerException( "implementation" );
691 }
692 if ( property == null )
693 {
694 throw new NullPointerException( "property" );
695 }
696
697 String modifier = "private";
698 final Properties specified = this.getModules().getSpecifiedProperties( implementation.getIdentifier() );
699
700 if ( specified != null && specified.getProperty( property.getName() ) != null )
701 {
702 modifier = "public";
703 }
704
705 return modifier;
706 }
707
708 /**
709 * Formats a text to a Javadoc comment.
710 *
711 * @param text The text to format to a Javadoc comment.
712 * @param linebreak The text to replace line breaks with.
713 *
714 * @return {@code text} formatted as a Javadoc comment.
715 *
716 * @throws NullPointerException if {@code text} or {@code linebreak} is {@code null}.
717 */
718 public String getJavadocComment( final Text text, final String linebreak )
719 {
720 if ( text == null )
721 {
722 throw new NullPointerException( "text" );
723 }
724 if ( linebreak == null )
725 {
726 throw new NullPointerException( "linebreak" );
727 }
728
729 String normalized = text.getValue();
730 normalized = normalized.replaceAll( "\\/\\*\\*", "/*" );
731 normalized = normalized.replaceAll( "\\*/", "/" );
732 normalized = normalized.replaceAll( "\n", "\n" + linebreak );
733 return StringEscapeUtils.escapeHtml( normalized );
734 }
735
736 /**
737 * Formats a string to a Java string with unicode escapes.
738 *
739 * @param str The string to format to a Java string or {@code null}.
740 *
741 * @return {@code str} formatted as a Java string or {@code null}.
742 */
743 public String getJavaString( final String str )
744 {
745 return StringEscapeUtils.escapeJava( str );
746 }
747
748 /**
749 * Gets a flag indicating if a given specification declares a Java class.
750 *
751 * @param specification The specification to test.
752 *
753 * @return {@code true} if {@code specification} is declaring the Java class with name
754 * {@code specification.getClazz()}; {@code false} if {@code specification} does not declare that class.
755 *
756 * @throws NullPointerException if {@code specification} is {@code null}.
757 */
758 public boolean isJavaClassDeclaration( final Specification specification )
759 {
760 if ( specification == null )
761 {
762 throw new NullPointerException( "specification" );
763 }
764
765 return specification.getClazz() != null && specification.getClazz().equals( specification.getIdentifier() );
766 }
767
768 /**
769 * Gets a flag indicating if a given implementation declares a Java class.
770 *
771 * @param implementation The implementation to test.
772 *
773 * @return {@code true} if {@code implementation} is declaring the Java class with name
774 * {@code implementation.getClazz()}; {@code false} if {@code implementation.getClazz()} is {@code null} or
775 * {@code implementation} does not declare that class.
776 *
777 * @throws NullPointerException if {@code implementation} is {@code null}.
778 */
779 public boolean isJavaClassDeclaration( final Implementation implementation )
780 {
781 if ( implementation == null )
782 {
783 throw new NullPointerException( "implementation" );
784 }
785
786 return implementation.getClazz() != null && implementation.getClazz().equals( implementation.getIdentifier() );
787 }
788
789 /**
790 * Gets a flag indicating if the class of a given specification is located in the Java default package.
791 *
792 * @param specification The specification to test.
793 *
794 * @return {@code true} if the class of {@code specification} is located in the Java default package; {@code false}
795 * if not.
796 *
797 * @throws NullPointerException if {@code specification} is {@code null}.
798 */
799 public boolean isJavaDefaultPackage( final Specification specification )
800 {
801 if ( specification == null )
802 {
803 throw new NullPointerException( "specification" );
804 }
805
806 return this.getJavaPackageName( specification ).length() == 0;
807 }
808
809 /**
810 * Gets a flag indicating if the class of a given implementation is located in the Java default package.
811 *
812 * @param implementation The implementation to test.
813 *
814 * @return {@code true} if the class of {@code implementation} is located in the Java default package; {@code false}
815 * if not.
816 *
817 * @throws NullPointerException if {@code implementation} is {@code null}.
818 */
819 public boolean isJavaDefaultPackage( final Implementation implementation )
820 {
821 if ( implementation == null )
822 {
823 throw new NullPointerException( "implementation" );
824 }
825
826 return this.getJavaPackageName( implementation ).length() == 0;
827 }
828
829 /**
830 * Gets the display language of a given language code.
831 *
832 * @param language The language code to get the display language of.
833 *
834 * @return The display language of {@code language}.
835 *
836 * @throws NullPointerException if {@code language} is {@code null}.
837 */
838 public String getDisplayLanguage( final String language )
839 {
840 if ( language == null )
841 {
842 throw new NullPointerException( "language" );
843 }
844
845 final Locale locale = new Locale( language );
846 return locale.getDisplayLanguage( locale );
847 }
848
849 /**
850 * Formats a calendar instance to a string.
851 *
852 * @param calendar The calendar to format.
853 *
854 * @return Date of {@code calendar} formatted using a short format style pattern.
855 *
856 * @throws NullPointerException if {@code calendar} is {@code null}.
857 *
858 * @see DateFormat#SHORT
859 */
860 public String getShortDate( final Calendar calendar )
861 {
862 if ( calendar == null )
863 {
864 throw new NullPointerException( "calendar" );
865 }
866
867 return DateFormat.getDateInstance( DateFormat.SHORT ).format( calendar.getTime() );
868 }
869
870 /**
871 * Formats a calendar instance to a string.
872 *
873 * @param calendar The calendar to format.
874 *
875 * @return Date of {@code calendar} formatted using a long format style pattern.
876 *
877 * @throws NullPointerException if {@code calendar} is {@code null}.
878 *
879 * @see DateFormat#LONG
880 */
881 public String getLongDate( final Calendar calendar )
882 {
883 if ( calendar == null )
884 {
885 throw new NullPointerException( "calendar" );
886 }
887
888 return DateFormat.getDateInstance( DateFormat.LONG ).format( calendar.getTime() );
889 }
890
891 /**
892 * Formats a calendar instance to a string.
893 *
894 * @param calendar The calendar to format.
895 *
896 * @return Time of {@code calendar} formatted using a short format style pattern.
897 *
898 * @throws NullPointerException if {@code calendar} is {@code null}.
899 *
900 * @see DateFormat#SHORT
901 */
902 public String getShortTime( final Calendar calendar )
903 {
904 if ( calendar == null )
905 {
906 throw new NullPointerException( "calendar" );
907 }
908
909 return DateFormat.getTimeInstance( DateFormat.SHORT ).format( calendar.getTime() );
910 }
911
912 /**
913 * Formats a calendar instance to a string.
914 *
915 * @param calendar The calendar to format.
916 *
917 * @return Time of {@code calendar} formatted using a long format style pattern.
918 *
919 * @throws NullPointerException if {@code calendar} is {@code null}.
920 *
921 * @see DateFormat#LONG
922 */
923 public String getLongTime( final Calendar calendar )
924 {
925 if ( calendar == null )
926 {
927 throw new NullPointerException( "calendar" );
928 }
929
930 return DateFormat.getTimeInstance( DateFormat.LONG ).format( calendar.getTime() );
931 }
932
933 /**
934 * Formats a calendar instance to a string.
935 *
936 * @param calendar The calendar to format.
937 *
938 * @return Date and time of {@code calendar} formatted using a short format style pattern.
939 *
940 * @throws NullPointerException if {@code calendar} is {@code null}.
941 *
942 * @see DateFormat#SHORT
943 */
944 public String getShortDateTime( final Calendar calendar )
945 {
946 if ( calendar == null )
947 {
948 throw new NullPointerException( "calendar" );
949 }
950
951 return DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT ).format( calendar.getTime() );
952 }
953
954 /**
955 * Formats a calendar instance to a string.
956 *
957 * @param calendar The calendar to format.
958 *
959 * @return Date and time of {@code calendar} formatted using a long format style pattern.
960 *
961 * @throws NullPointerException if {@code calendar} is {@code null}.
962 *
963 * @see DateFormat#LONG
964 */
965 public String getLongDateTime( final Calendar calendar )
966 {
967 if ( calendar == null )
968 {
969 throw new NullPointerException( "calendar" );
970 }
971
972 return DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG ).format( calendar.getTime() );
973 }
974
975 /**
976 * Gets a string describing the range of years for given calendars.
977 *
978 * @param start The start of the range.
979 * @param end The end of the range.
980 *
981 * @return Formatted range of the years of {@code start} and {@code end}.
982 *
983 * @throws NullPointerException if {@code start} or {@code end} is {@code null}.
984 */
985 public String getYears( final Calendar start, final Calendar end )
986 {
987 if ( start == null )
988 {
989 throw new NullPointerException( "start" );
990 }
991 if ( end == null )
992 {
993 throw new NullPointerException( "end" );
994 }
995
996 final Format yearFormat = new SimpleDateFormat( "yyyy" );
997 final int s = start.get( Calendar.YEAR );
998 final int e = end.get( Calendar.YEAR );
999 final StringBuilder years = new StringBuilder();
1000
1001 if ( s != e )
1002 {
1003 if ( s < e )
1004 {
1005 years.append( yearFormat.format( start.getTime() ) ).append( " - " ).
1006 append( yearFormat.format( end.getTime() ) );
1007
1008 }
1009 else
1010 {
1011 years.append( yearFormat.format( end.getTime() ) ).append( " - " ).
1012 append( yearFormat.format( start.getTime() ) );
1013
1014 }
1015 }
1016 else
1017 {
1018 years.append( yearFormat.format( start.getTime() ) );
1019 }
1020
1021 return years.toString();
1022 }
1023
1024 /**
1025 * Gets the modules of the instance.
1026 *
1027 * @return The modules of the instance.
1028 *
1029 * @see #setModules(org.jomc.model.Modules)
1030 */
1031 public Modules getModules()
1032 {
1033 if ( this.modules == null )
1034 {
1035 this.modules = new Modules();
1036 }
1037
1038 return this.modules;
1039 }
1040
1041 /**
1042 * Sets the modules of the instance.
1043 *
1044 * @param value The new modules of the instance.
1045 *
1046 * @see #getModules()
1047 */
1048 public void setModules( final Modules value )
1049 {
1050 this.modules = value;
1051 }
1052
1053 /**
1054 * Gets the model manager of the instance.
1055 *
1056 * @return The model manager of the instance.
1057 *
1058 * @see #setModelManager(org.jomc.model.ModelManager)
1059 */
1060 public ModelManager getModelManager()
1061 {
1062 if ( this.modelManager == null )
1063 {
1064 this.modelManager = new DefaultModelManager();
1065 }
1066
1067 return this.modelManager;
1068 }
1069
1070 /**
1071 * Sets the model manager of the instance.
1072 *
1073 * @param value The new model manager of the instance.
1074 *
1075 * @see #getModelManager()
1076 */
1077 public void setModelManager( final ModelManager value )
1078 {
1079 this.modelManager = value;
1080 }
1081
1082 /**
1083 * Gets the {@code VelocityEngine} used for generating source code.
1084 *
1085 * @return The {@code VelocityEngine} used for generating source code.
1086 *
1087 * @throws IllegalStateException if initializing a new velocity engine fails.
1088 *
1089 * @see #setVelocityEngine(org.apache.velocity.app.VelocityEngine)
1090 */
1091 public VelocityEngine getVelocityEngine()
1092 {
1093 if ( this.velocityEngine == null )
1094 {
1095 try
1096 {
1097 final java.util.Properties props = new java.util.Properties();
1098 props.put( "resource.loader", "class" );
1099 props.put( "class.resource.loader.class", VELOCITY_RESOURCE_LOADER );
1100 props.put( "runtime.references.strict", Boolean.TRUE.toString() );
1101
1102 final VelocityEngine engine = new VelocityEngine();
1103 engine.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new LogChute()
1104 {
1105
1106 public void init( final RuntimeServices runtimeServices ) throws Exception
1107 {
1108 }
1109
1110 public void log( final int level, final String message )
1111 {
1112 this.log( level, message, null );
1113 }
1114
1115 public void log( final int level, final String message, final Throwable throwable )
1116 {
1117 JomcTool.this.log( this.toLevel( level ), message, throwable );
1118 }
1119
1120 public boolean isLevelEnabled( final int level )
1121 {
1122 return true;
1123 }
1124
1125 private Level toLevel( final int logChuteLevel )
1126 {
1127 switch ( logChuteLevel )
1128 {
1129 case LogChute.DEBUG_ID:
1130 return Level.FINE;
1131
1132 case LogChute.ERROR_ID:
1133 return Level.SEVERE;
1134
1135 case LogChute.INFO_ID:
1136 return Level.INFO;
1137
1138 case LogChute.TRACE_ID:
1139 return Level.FINER;
1140
1141 case LogChute.WARN_ID:
1142 return Level.WARNING;
1143
1144 default:
1145 return Level.FINEST;
1146
1147 }
1148 }
1149
1150 } );
1151
1152 engine.init( props );
1153 this.velocityEngine = engine;
1154 }
1155 catch ( final Exception e )
1156 {
1157 throw new IllegalStateException( e );
1158 }
1159 }
1160
1161 return this.velocityEngine;
1162 }
1163
1164 /**
1165 * Sets the {@code VelocityEngine} of the instance.
1166 *
1167 * @param value The new {@code VelocityEngine} of the instance.
1168 *
1169 * @see #getVelocityEngine()
1170 */
1171 public void setVelocityEngine( final VelocityEngine value )
1172 {
1173 this.velocityEngine = value;
1174 }
1175
1176 /**
1177 * Gets the velocity context used for merging templates.
1178 *
1179 * @return The velocity context used for merging templates.
1180 */
1181 public VelocityContext getVelocityContext()
1182 {
1183 final Date now = new Date();
1184 final VelocityContext ctx = new VelocityContext();
1185 ctx.put( "modules", this.getModules() );
1186 ctx.put( "tool", this );
1187 ctx.put( "calendar", Calendar.getInstance() );
1188 ctx.put( "now", new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ" ).format( now ) );
1189 ctx.put( "year", new SimpleDateFormat( "yyyy" ).format( now ) );
1190 ctx.put( "month", new SimpleDateFormat( "MM" ).format( now ) );
1191 ctx.put( "day", new SimpleDateFormat( "dd" ).format( now ) );
1192 ctx.put( "hour", new SimpleDateFormat( "HH" ).format( now ) );
1193 ctx.put( "minute", new SimpleDateFormat( "mm" ).format( now ) );
1194 ctx.put( "second", new SimpleDateFormat( "ss" ).format( now ) );
1195 ctx.put( "timezone", new SimpleDateFormat( "Z" ).format( now ) );
1196 return ctx;
1197 }
1198
1199 /**
1200 * Gets the encoding to use for reading templates.
1201 *
1202 * @return The encoding to use for reading templates.
1203 *
1204 * @see #setTemplateEncoding(java.lang.String)
1205 */
1206 public String getTemplateEncoding()
1207 {
1208 if ( this.templateEncoding == null )
1209 {
1210 this.templateEncoding = this.getMessage( "buildSourceEncoding", null );
1211 }
1212
1213 return this.templateEncoding;
1214 }
1215
1216 /**
1217 * Sets the encoding to use for reading templates.
1218 *
1219 * @param value The encoding to use for reading templates.
1220 *
1221 * @see #getTemplateEncoding()
1222 */
1223 public void setTemplateEncoding( final String value )
1224 {
1225 this.templateEncoding = value;
1226 }
1227
1228 /**
1229 * Gets the encoding to use for reading files.
1230 *
1231 * @return The encoding to use for reading files.
1232 *
1233 * @see #setInputEncoding(java.lang.String)
1234 */
1235 public String getInputEncoding()
1236 {
1237 if ( this.inputEncoding == null )
1238 {
1239 this.inputEncoding = new InputStreamReader( new ByteArrayInputStream( NO_BYTES ) ).getEncoding();
1240 this.log( Level.FINE, this.getMessage( "defaultInputEncoding", new Object[]
1241 {
1242 this.inputEncoding
1243 } ), null );
1244
1245 }
1246
1247 return this.inputEncoding;
1248 }
1249
1250 /**
1251 * Sets the encoding to use for reading files.
1252 *
1253 * @param value The encoding to use for reading files.
1254 *
1255 * @see #getInputEncoding()
1256 */
1257 public void setInputEncoding( final String value )
1258 {
1259 this.inputEncoding = value;
1260 }
1261
1262 /**
1263 * Gets the encoding to use for writing files.
1264 *
1265 * @return The encoding to use for writing files.
1266 *
1267 * @see #setOutputEncoding(java.lang.String)
1268 */
1269 public String getOutputEncoding()
1270 {
1271 if ( this.outputEncoding == null )
1272 {
1273 this.outputEncoding = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding();
1274 this.log( Level.FINE, this.getMessage( "defaultOutputEncoding", new Object[]
1275 {
1276 this.outputEncoding
1277 } ), null );
1278
1279 }
1280
1281 return this.outputEncoding;
1282 }
1283
1284 /**
1285 * Sets the encoding to use for writing files.
1286 *
1287 * @param value The encoding to use for writing files.
1288 *
1289 * @see #getOutputEncoding()
1290 */
1291 public void setOutputEncoding( final String value )
1292 {
1293 this.outputEncoding = value;
1294 }
1295
1296 /**
1297 * Gets the profile of the instance.
1298 *
1299 * @return The profile of the instance.
1300 *
1301 * @see #setProfile(java.lang.String)
1302 */
1303 public String getProfile()
1304 {
1305 if ( this.profile == null )
1306 {
1307 this.profile = "default";
1308 this.log( Level.FINE, this.getMessage( "defaultProfile", new Object[]
1309 {
1310 this.profile
1311 } ), null );
1312
1313 }
1314
1315 return this.profile;
1316 }
1317
1318 /**
1319 * Sets the profile of the instance.
1320 *
1321 * @param value The profile of the instance.
1322 *
1323 * @see #getProfile()
1324 */
1325 public void setProfile( final String value )
1326 {
1327 this.profile = value;
1328 }
1329
1330 /**
1331 * Gets a velocity template for a given name.
1332 * <p>This method returns the template corresponding to the profile of the instance. If that template is not found,
1333 * the template corresponding to the default profile is returned so that only templates differing from the default
1334 * templates need to be provided when exchanging templates.</p>
1335 *
1336 * @param templateName The name of the template to get.
1337 *
1338 * @return The template matching {@code templateName}.
1339 *
1340 * @throws NullPointerException if {@code templateName} is {@code null}.
1341 * @throws IOException if getting the template fails.
1342 *
1343 * @see #getProfile()
1344 * @see #getTemplateEncoding()
1345 */
1346 public Template getVelocityTemplate( final String templateName ) throws IOException
1347 {
1348 if ( templateName == null )
1349 {
1350 throw new NullPointerException( "templateName" );
1351 }
1352
1353 try
1354 {
1355 return this.getVelocityEngine().getTemplate( TEMPLATE_PREFIX + this.getProfile() + "/" + templateName,
1356 this.getTemplateEncoding() );
1357
1358 }
1359 catch ( final ResourceNotFoundException e )
1360 {
1361 this.log( Level.CONFIG, this.getMessage( "templateNotFound", new Object[]
1362 {
1363 templateName, TEMPLATE_PREFIX + this.getProfile() + "/" + templateName
1364 } ), e );
1365
1366 try
1367 {
1368 return this.getVelocityEngine().getTemplate( TEMPLATE_PREFIX + "default/" + templateName,
1369 this.getTemplateEncoding() );
1370
1371 }
1372 catch ( final Exception e2 )
1373 {
1374 throw (IOException) new IOException( e2.getMessage() ).initCause( e2 );
1375 }
1376 }
1377 catch ( final Exception e )
1378 {
1379 throw (IOException) new IOException( e.getMessage() ).initCause( e );
1380 }
1381 }
1382
1383 /**
1384 * Notifies registered listeners.
1385 *
1386 * @param level The level of the event.
1387 * @param message The message of the event.
1388 * @param throwable The throwable of the event.
1389 *
1390 * @see #getListeners()
1391 */
1392 protected void log( final Level level, final String message, final Throwable throwable )
1393 {
1394 for ( Listener l : this.getListeners() )
1395 {
1396 l.onLog( level, message, throwable );
1397 }
1398 }
1399
1400 private String getJavaPackageName( final String identifier )
1401 {
1402 if ( identifier == null )
1403 {
1404 throw new NullPointerException( "identifier" );
1405 }
1406
1407 final int idx = identifier.lastIndexOf( '.' );
1408 return idx != -1 ? identifier.substring( 0, idx ) : "";
1409 }
1410
1411 private String getMessage( final String key, final Object args )
1412 {
1413 if ( key == null )
1414 {
1415 throw new NullPointerException( "key" );
1416 }
1417
1418 final ResourceBundle b = ResourceBundle.getBundle( JomcTool.class.getName().replace( '.', '/' ) );
1419 return args == null ? b.getString( key ) : new MessageFormat( b.getString( key ) ).format( args );
1420 }
1421
1422 }