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