001 /*
002 * Copyright (c) 2009 The JOMC Project
003 * Copyright (c) 2005 Christian Schulte <schulte2005@users.sourceforge.net>
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: ResourceFileProcessor.java 2179 2010-06-28 17:49:49Z schulte2005 $
031 *
032 */
033 package org.jomc.tools;
034
035 import java.io.File;
036 import java.io.FileOutputStream;
037 import java.io.IOException;
038 import java.io.OutputStream;
039 import java.text.MessageFormat;
040 import java.util.HashMap;
041 import java.util.Locale;
042 import java.util.Map;
043 import java.util.Properties;
044 import java.util.ResourceBundle;
045 import java.util.logging.Level;
046 import org.apache.velocity.VelocityContext;
047 import org.jomc.model.Implementation;
048 import org.jomc.model.Message;
049 import org.jomc.model.Messages;
050 import org.jomc.model.Module;
051 import org.jomc.model.Specification;
052 import org.jomc.model.Text;
053
054 /**
055 * Processes resource files.
056 *
057 * <p><b>Use cases</b><br/><ul>
058 * <li>{@link #writeResourceBundleResourceFiles(File) }</li>
059 * <li>{@link #writeResourceBundleResourceFiles(Module, File) }</li>
060 * <li>{@link #writeResourceBundleResourceFiles(Specification, File) }</li>
061 * <li>{@link #writeResourceBundleResourceFiles(Implementation, File) }</li>
062 * </ul></p>
063 *
064 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
065 * @version $Id: ResourceFileProcessor.java 2179 2010-06-28 17:49:49Z schulte2005 $
066 *
067 * @see #getModules()
068 */
069 public class ResourceFileProcessor extends JomcTool
070 {
071
072 /** The language of the default language properties file of generated resource bundle resources. */
073 private Locale resourceBundleDefaultLocale;
074
075 /** Creates a new {@code ResourceFileProcessor} instance. */
076 public ResourceFileProcessor()
077 {
078 super();
079 }
080
081 /**
082 * Creates a new {@code ResourceFileProcessor} instance taking a {@code ResourceFileProcessor} instance to
083 * initialize the instance with.
084 *
085 * @param tool The instance to initialize the new instance with.
086 *
087 * @throws NullPointerException if {@code tool} is {@code null}.
088 * @throws IOException if copying {@code tool} fails.
089 */
090 public ResourceFileProcessor( final ResourceFileProcessor tool ) throws IOException
091 {
092 super( tool );
093 this.resourceBundleDefaultLocale = tool.resourceBundleDefaultLocale;
094 }
095
096 /**
097 * Gets the language of the default language properties file of generated resource bundle resource files.
098 *
099 * @return The language of the default language properties file of generated resource bundle resource files.
100 *
101 * @see #setResourceBundleDefaultLocale(java.util.Locale)
102 */
103 public final Locale getResourceBundleDefaultLocale()
104 {
105 if ( this.resourceBundleDefaultLocale == null )
106 {
107 this.resourceBundleDefaultLocale = Locale.getDefault();
108
109 if ( this.isLoggable( Level.CONFIG ) )
110 {
111 this.log( Level.CONFIG, getMessage( "resourceBundleDefaultLocale", this.getClass().getName(),
112 this.resourceBundleDefaultLocale ), null );
113
114 }
115 }
116
117 return this.resourceBundleDefaultLocale;
118 }
119
120 /**
121 * Sets the language of the default language properties file of generated resource bundle resource files.
122 *
123 * @param value The language of the default language properties file of generated resource bundle resource files.
124 *
125 * @see #getResourceBundleDefaultLocale()
126 */
127 public final void setResourceBundleDefaultLocale( final Locale value )
128 {
129 this.resourceBundleDefaultLocale = value;
130 }
131
132 /**
133 * Writes resource bundle resource files of the modules of the instance to a given directory.
134 *
135 * @param resourcesDirectory The directory to write resource bundle resource files to.
136 *
137 * @throws NullPointerException if {@code resourcesDirectory} is {@code null}.
138 * @throws IOException if writing resource bundle resource files fails.
139 *
140 * @see #writeResourceBundleResourceFiles(org.jomc.model.Module, java.io.File)
141 */
142 public void writeResourceBundleResourceFiles( final File resourcesDirectory ) throws IOException
143 {
144 if ( resourcesDirectory == null )
145 {
146 throw new NullPointerException( "resourcesDirectory" );
147 }
148
149 for ( Module m : this.getModules().getModule() )
150 {
151 this.writeResourceBundleResourceFiles( m, resourcesDirectory );
152 }
153 }
154
155 /**
156 * Writes resource bundle resource files of a given module from the modules of the instance to a given directory.
157 *
158 * @param module The module to process.
159 * @param resourcesDirectory The directory to write resource bundle resource files to.
160 *
161 * @throws NullPointerException if {@code module} or {@code resourcesDirectory} is {@code null}.
162 * @throws IOException if writing resource bundle resource files fails.
163 *
164 * @see #writeResourceBundleResourceFiles(org.jomc.model.Specification, java.io.File)
165 * @see #writeResourceBundleResourceFiles(org.jomc.model.Implementation, java.io.File)
166 */
167 public void writeResourceBundleResourceFiles( final Module module, final File resourcesDirectory )
168 throws IOException
169 {
170 if ( module == null )
171 {
172 throw new NullPointerException( "module" );
173 }
174 if ( resourcesDirectory == null )
175 {
176 throw new NullPointerException( "resourcesDirectory" );
177 }
178
179 assert this.getModules().getModule( module.getName() ) != null : "Module '" + module.getName() + "' not found.";
180
181 if ( module.getSpecifications() != null )
182 {
183 for ( Specification s : module.getSpecifications().getSpecification() )
184 {
185 this.writeResourceBundleResourceFiles( s, resourcesDirectory );
186 }
187 }
188
189 if ( module.getImplementations() != null )
190 {
191 for ( Implementation i : module.getImplementations().getImplementation() )
192 {
193 this.writeResourceBundleResourceFiles( i, resourcesDirectory );
194 }
195 }
196 }
197
198 /**
199 * Writes resource bundle resource files of a given specification from the modules of the instance to a directory.
200 *
201 * @param specification The specification to process.
202 * @param resourcesDirectory The directory to write resource bundle resource files to.
203 *
204 * @throws NullPointerException if {@code specification} or {@code resourcesDirectory} is {@code null}.
205 * @throws IOException if writing resource bundle resource files fails.
206 *
207 * @see #getResourceBundleResources(org.jomc.model.Specification)
208 */
209 public void writeResourceBundleResourceFiles( final Specification specification, final File resourcesDirectory )
210 throws IOException
211 {
212 if ( specification == null )
213 {
214 throw new NullPointerException( "implementation" );
215 }
216 if ( resourcesDirectory == null )
217 {
218 throw new NullPointerException( "resourcesDirectory" );
219 }
220
221 assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
222 "Specification '" + specification.getIdentifier() + "' not found.";
223
224 if ( specification.isClassDeclaration() )
225 {
226 this.assertValidTemplates( specification );
227
228 final String bundlePath =
229 this.getJavaTypeName( specification, true ).replace( '.', File.separatorChar );
230
231 this.writeResourceBundleResourceFiles(
232 this.getResourceBundleResources( specification ), resourcesDirectory, bundlePath );
233
234 }
235 }
236
237 /**
238 * Writes resource bundle resource files of a given implementation from the modules of the instance to a directory.
239 *
240 * @param implementation The implementation to process.
241 * @param resourcesDirectory The directory to write resource bundle resource files to.
242 *
243 * @throws NullPointerException if {@code implementation} or {@code resourcesDirectory} is {@code null}.
244 * @throws IOException if writing resource bundle resource files fails.
245 *
246 * @see #getResourceBundleResources(org.jomc.model.Implementation)
247 */
248 public void writeResourceBundleResourceFiles( final Implementation implementation, final File resourcesDirectory )
249 throws IOException
250 {
251 if ( implementation == null )
252 {
253 throw new NullPointerException( "implementation" );
254 }
255 if ( resourcesDirectory == null )
256 {
257 throw new NullPointerException( "resourcesDirectory" );
258 }
259
260 assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
261 "Implementation '" + implementation.getIdentifier() + "' not found.";
262
263 if ( implementation.isClassDeclaration() )
264 {
265 this.assertValidTemplates( implementation );
266
267 final String bundlePath =
268 this.getJavaTypeName( implementation, true ).replace( '.', File.separatorChar );
269
270 this.writeResourceBundleResourceFiles(
271 this.getResourceBundleResources( implementation ), resourcesDirectory, bundlePath );
272
273 }
274 }
275
276 /**
277 * Gets resource bundle properties resources of a given specification.
278 *
279 * @param specification The specification to get resource bundle properties resources of.
280 *
281 * @return Resource bundle properties resources of {@code specification}.
282 *
283 * @throws NullPointerException if {@code specification} is {@code null}.
284 * @throws IOException if getting the resource bundle properties resources fails.
285 */
286 public Map<Locale, Properties> getResourceBundleResources( final Specification specification )
287 throws IOException
288 {
289 if ( specification == null )
290 {
291 throw new NullPointerException( "specification" );
292 }
293
294 assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
295 "Specification '" + specification.getIdentifier() + "' not found.";
296
297 return new HashMap<Locale, java.util.Properties>();
298 }
299
300 /**
301 * Gets resource bundle properties resources of a given implementation.
302 *
303 * @param implementation The implementation to get resource bundle properties resources of.
304 *
305 * @return Resource bundle properties resources of {@code implementation}.
306 *
307 * @throws NullPointerException if {@code implementation} is {@code null}.
308 * @throws IOException if getting the resource bundle properties resources fails.
309 */
310 public Map<Locale, Properties> getResourceBundleResources( final Implementation implementation )
311 throws IOException
312 {
313 if ( implementation == null )
314 {
315 throw new NullPointerException( "implementation" );
316 }
317
318 assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
319 "Implementation '" + implementation.getIdentifier() + "' not found.";
320
321 final Map<Locale, java.util.Properties> properties = new HashMap<Locale, java.util.Properties>( 10 );
322 final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
323
324 if ( messages != null )
325 {
326 for ( Message message : messages.getMessage() )
327 {
328 if ( message.getTemplate() != null )
329 {
330 for ( Text text : message.getTemplate().getText() )
331 {
332 final Locale locale = new Locale( text.getLanguage().toLowerCase() );
333 Properties bundleProperties = properties.get( locale );
334
335 if ( bundleProperties == null )
336 {
337 bundleProperties = new Properties();
338 properties.put( locale, bundleProperties );
339 }
340
341 bundleProperties.setProperty( message.getName(), text.getValue() );
342 }
343 }
344 }
345 }
346
347 return properties;
348 }
349
350 private void writeResourceBundleResourceFiles( final Map<Locale, Properties> resources,
351 final File resourcesDirectory, final String bundlePath )
352 throws IOException
353 {
354 if ( resources == null )
355 {
356 throw new NullPointerException( "resources" );
357 }
358 if ( resourcesDirectory == null )
359 {
360 throw new NullPointerException( "resourcesDirectory" );
361 }
362 if ( bundlePath == null )
363 {
364 throw new NullPointerException( "bundlePath" );
365 }
366
367 Properties defProperties = null;
368 Properties fallbackProperties = null;
369
370 final VelocityContext ctx = this.getVelocityContext();
371 final String toolName = ctx.get( "toolName" ).toString();
372 final String toolVersion = ctx.get( "toolVersion" ).toString();
373 final String toolUrl = ctx.get( "toolUrl" ).toString();
374
375 for ( Map.Entry<Locale, Properties> e : resources.entrySet() )
376 {
377 final String language = e.getKey().getLanguage().toLowerCase();
378 final Properties p = e.getValue();
379 final File file = new File( resourcesDirectory, bundlePath + "_" + language + ".properties" );
380
381 if ( this.getResourceBundleDefaultLocale().getLanguage().equalsIgnoreCase( language ) )
382 {
383 defProperties = p;
384 }
385
386 fallbackProperties = p;
387
388 if ( !file.getParentFile().exists() && !file.getParentFile().mkdirs() )
389 {
390 throw new IOException( getMessage( "failedCreatingDirectory",
391 file.getParentFile().getAbsolutePath() ) );
392
393 }
394
395 if ( this.isLoggable( Level.INFO ) )
396 {
397 this.log( Level.INFO, getMessage( "writing", file.getCanonicalPath() ), null );
398 }
399
400 OutputStream out = null;
401 try
402 {
403 out = new FileOutputStream( file );
404 p.store( out, toolName + ' ' + toolVersion + " - See " + toolUrl );
405 }
406 finally
407 {
408 if ( out != null )
409 {
410 out.close();
411 }
412 }
413 }
414
415 if ( defProperties == null )
416 {
417 defProperties = fallbackProperties;
418 }
419
420 if ( defProperties != null )
421 {
422 final File file = new File( resourcesDirectory, bundlePath + ".properties" );
423 if ( !file.getParentFile().exists() && !file.getParentFile().mkdirs() )
424 {
425 throw new IOException( getMessage( "failedCreatingDirectory",
426 file.getParentFile().getAbsolutePath() ) );
427
428 }
429
430 if ( this.isLoggable( Level.INFO ) )
431 {
432 this.log( Level.INFO, getMessage( "writing", file.getCanonicalPath() ), null );
433 }
434
435 OutputStream out = null;
436 try
437 {
438 out = new FileOutputStream( file );
439 defProperties.store( out, toolName + ' ' + toolVersion + " - See " + toolUrl );
440 }
441 finally
442 {
443 if ( out != null )
444 {
445 out.close();
446 }
447 }
448 }
449 }
450
451 private void assertValidTemplates( final Specification specification )
452 {
453 if ( specification == null )
454 {
455 throw new NullPointerException( "specification" );
456 }
457 }
458
459 private void assertValidTemplates( final Implementation implementation )
460 {
461 if ( implementation == null )
462 {
463 throw new NullPointerException( "implementation" );
464 }
465
466 final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
467
468 if ( messages != null )
469 {
470 for ( Message m : messages.getMessage() )
471 {
472 if ( m.getTemplate() != null )
473 {
474 for ( Text t : m.getTemplate().getText() )
475 {
476 new MessageFormat( t.getValue() );
477 }
478 }
479 }
480 }
481 }
482
483 private static String getMessage( final String key, final Object... arguments )
484 {
485 if ( key == null )
486 {
487 throw new NullPointerException( "key" );
488 }
489
490 return MessageFormat.format( ResourceBundle.getBundle(
491 ResourceFileProcessor.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
492
493 }
494
495 }