View Javadoc

1   /*
2    * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.properties.api.core.domain;
17  
18  import java.io.Serializable;
19  import java.net.URL;
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Locale;
23  
24  import javax.annotation.CheckForNull;
25  import javax.annotation.concurrent.ThreadSafe;
26  
27  import org.apache.commons.lang.LocaleUtils;
28  import org.apache.commons.lang.StringUtils;
29  import org.apache.commons.lang.builder.ToStringBuilder;
30  
31  import de.smartics.properties.api.core.context.alias.AliasTraverser;
32  import de.smartics.properties.api.core.context.alias.DuplicateAliasException;
33  import de.smartics.properties.api.core.context.alias.PropertyAliasMapping;
34  import de.smartics.properties.api.core.context.alias.UnknownAliasException;
35  import de.smartics.util.lang.Arguments;
36  import de.smartics.util.lang.BlankArgumentException;
37  
38  /**
39   * Defines the configuration for smartics properties configuration.
40   * <p>
41   * A context provides information for all its properties. Not all properties of
42   * an application may refer to the same context.
43   * </p>
44   *
45   * @impl For testing purposes please refer to
46   *       <code>de.smartics.properties.test.core.PropertiesContextBuilder</code>.
47   */
48  @ThreadSafe
49  public final class PropertiesContext implements Serializable
50  { // NOPMD
51    // ********************************* Fields *********************************
52  
53    // --- constants ------------------------------------------------------------
54  
55    /**
56     * The class version identifier.
57     */
58    private static final long serialVersionUID = 1L;
59  
60    /**
61     * The path to the folder within <code>META-INF</code> where properties
62     * resources are located.
63     * <p>
64     * The value of this constant is {@value}.
65     * </p>
66     */
67    public static final String META_INF_HOME = "META-INF/smartics-properties";
68  
69    /**
70     * The name of the properties configuration file that provides information for
71     * declarations. This file provides information in archives that provide
72     * property meta data (descriptors).
73     * <p>
74     * The value of this constant is {@value}.
75     * </p>
76     */
77    public static final String DECLARATION_FILE_NAME = "declaration.xml";
78  
79    /**
80     * The name of the properties configuration file that provides information for
81     * definitions. This file provides information in archives that provide
82     * property values.
83     * <p>
84     * The value of this constant is {@value}.
85     * </p>
86     */
87    public static final String DEFINITION_FILE_NAME = "definition.xml";
88  
89    /**
90     * The default location of the properties declaration configuration file.
91     * <p>
92     * The value of this constant is {@value}.
93     * </p>
94     */
95    public static final String DECLARATION_FILE = META_INF_HOME + '/'
96                                                  + DECLARATION_FILE_NAME;
97  
98    /**
99     * The default location of the properties definition configuration file.
100    * <p>
101    * The value of this constant is {@value}.
102    * </p>
103    */
104   public static final String DEFINITION_FILE = META_INF_HOME + '/'
105                                                + DEFINITION_FILE_NAME;
106 
107   /**
108    * The location of the property reports within the <code>META-INF</code>
109    * folder.
110    * <p>
111    * The value of this constant is {@value}.
112    * </p>
113    */
114   private static final String META_INF_PROPERTY_REPORT = META_INF_HOME
115                                                          + "/property-report/";
116 
117   /**
118    * The location of the property set reports within the <code>META-INF</code>
119    * folder.
120    * <p>
121    * The value of this constant is {@value}.
122    * </p>
123    */
124   public static final String META_INF_PROPERTY_SET_REPORT =
125       META_INF_HOME + "/property-set-report/";
126 
127   /**
128    * Specifies the default report location on the home page.
129    * <p>
130    * The value of this constant is {@value}.
131    * </p>
132    */
133   private static final String DEFAULT_REPORT_LOCATION = "/property";
134 
135   // --- members --------------------------------------------------------------
136 
137   /**
138    * The list of supported locales. The list contains locales the context
139    * provides localized information for.
140    *
141    * @serial
142    */
143   private final List<Locale> locales;
144 
145   /**
146    * The URL to the home page of the project. May be <code>null</code>.
147    *
148    * @serial
149    */
150   private final String homePageUrl;
151 
152   /**
153    * The URL to the root directory of smartics properties reports. May be
154    * <code>null</code>.
155    *
156    * @serial
157    */
158   private final String propertiesReportUrl;
159 
160   /**
161    * The mapping of alias names of property reports to their physical names.
162    *
163    * @impl Although {@link PropertyAliasMapping} is not thread-safe, this
164    *       instance provides no write-access to it.
165    * @serial
166    */
167   private final PropertyAliasMapping aliasMapping;
168 
169   // ****************************** Initializer *******************************
170 
171   // ****************************** Constructors ******************************
172 
173   /**
174    * Default constructor.
175    */
176   private PropertiesContext(final Builder builder)
177   {
178     this.locales = builder.locales;
179     this.homePageUrl = builder.homePageUrl;
180     this.propertiesReportUrl = builder.propertiesReportUrl;
181     this.aliasMapping = builder.aliasMapping;
182   }
183 
184   // ****************************** Inner Classes *****************************
185 
186   /**
187    * The builder of {@link PropertiesContext}.
188    */
189   public static final class Builder
190   {
191     // ******************************** Fields ********************************
192 
193     // --- constants ----------------------------------------------------------
194 
195     // --- members ------------------------------------------------------------
196 
197     /**
198      * The list of supported locales. The list contains locales the context
199      * provides localized information for.
200      */
201     private List<Locale> locales = new ArrayList<Locale>();
202 
203     /**
204      * The URL to the home page of the project.
205      */
206     private String homePageUrl;
207 
208     /**
209      * The URL to the root directory of smartics properties reports.
210      */
211     private String propertiesReportUrl;
212 
213     /**
214      * The mapping of alias names of property reports to their physical names.
215      */
216     private final PropertyAliasMapping aliasMapping =
217         new PropertyAliasMapping();
218 
219     // ***************************** Initializer ******************************
220 
221     // ***************************** Constructors *****************************
222 
223     // ***************************** Inner Classes ****************************
224 
225     // ******************************** Methods *******************************
226 
227     // --- init ---------------------------------------------------------------
228 
229     // --- get&set ------------------------------------------------------------
230 
231     /**
232      * Sets the list of supported locales. The list contains locales the context
233      * provides localized information for.
234      *
235      * @param locales the list of supported locales.
236      * @return a reference to the builder.
237      * @throws NullPointerException if {@code locales} is <code>null</code>.
238      */
239     public Builder withLocales(final List<Locale> locales)
240       throws NullPointerException
241     {
242       Arguments.checkNotNull("locales", locales);
243       this.locales = locales;
244       return this;
245     }
246 
247     /**
248      * Sets the URL to the home page of the project.
249      *
250      * @param homePageUrl the URL to the home page of the project.
251      * @return a reference to the builder.
252      * @throws IllegalArgumentException if {@code homePageUrl} is blank.
253      */
254     public Builder withHomePageUrl(final String homePageUrl)
255       throws IllegalArgumentException
256     {
257       Arguments.checkNotBlank("homePageUrl", homePageUrl);
258       this.homePageUrl = normalizeUrl(homePageUrl);
259       return this;
260     }
261 
262     /**
263      * Sets the URL to the root directory of smartics properties reports.
264      *
265      * @param propertiesReportUrl the URL to the root directory of smartics
266      *          properties reports.
267      * @return a reference to the builder.
268      * @throws IllegalArgumentException if {@code propertiesReportUrl} is blank.
269      */
270     public Builder withPropertiesReportUrl(final String propertiesReportUrl)
271       throws IllegalArgumentException
272     {
273       Arguments.checkNotBlank("propertiesReportUrl", propertiesReportUrl);
274       this.propertiesReportUrl = normalizeUrl(propertiesReportUrl);
275       return this;
276     }
277 
278     /**
279      * Adds a new alias to the given physical resource.
280      *
281      * @param alias the new alias.
282      * @param physical the resource the alias refers to.
283      * @return a reference to the builder.
284      * @throws BlankArgumentException if either {@code alias} or
285      *           {@code physical} is blank.
286      * @throws DuplicateAliasException if there is already an alias registered
287      *           that points to a different physical resource.
288      */
289     public Builder withAlias(final String alias, final String physical)
290       throws BlankArgumentException, DuplicateAliasException
291     {
292       aliasMapping.add(alias, physical);
293       return this;
294     }
295 
296     // --- business -----------------------------------------------------------
297 
298     /**
299      * Creates the instance.
300      *
301      * @return the new instance.
302      */
303     public PropertiesContext build()
304     {
305       return new PropertiesContext(this);
306     }
307 
308     // --- object basics ------------------------------------------------------
309   }
310 
311   // ********************************* Methods ********************************
312 
313   // --- init -----------------------------------------------------------------
314 
315   // --- factory --------------------------------------------------------------
316 
317   /**
318    * Creates an empty context.
319    *
320    * @return an empty context.
321    */
322   public static PropertiesContext createEmptyContext()
323   {
324     return new Builder().build();
325   }
326 
327   // --- get&set --------------------------------------------------------------
328 
329   /**
330    * Returns the URL to the home page of the project.
331    *
332    * @return the URL to the home page of the project. May be <code>null</code>.
333    */
334   @CheckForNull
335   public String getHomePageUrl()
336   {
337     return homePageUrl;
338   }
339 
340   /**
341    * Returns the URL to the root directory of smartics properties reports.
342    *
343    * @return the URL to the root directory of smartics properties reports. May
344    *         be <code>null</code>.
345    */
346   @CheckForNull
347   public String getPropertiesReportUrl()
348   {
349     if (propertiesReportUrl == null && homePageUrl != null)
350     {
351       return homePageUrl + DEFAULT_REPORT_LOCATION;
352     }
353     return propertiesReportUrl;
354   }
355 
356   /**
357    * Returns the URL to the index document of smartics properties reports.
358    *
359    * @return the URL to the index document of smartics properties reports. May
360    *         be <code>null</code>.
361    */
362   @CheckForNull
363   public String getPropertiesReportIndexUrl()
364   {
365     return createReportUrl("smartics-properties-report");
366   }
367 
368   /**
369    * Returns the list of supported locales. The list contains locales the
370    * context provides localized information for.
371    *
372    * @return the list of supported locales.
373    */
374   public List<Locale> getLocales()
375   {
376     return locales;
377   }
378 
379   // --- business -------------------------------------------------------------
380 
381   private static String normalizeUrl(final String url)
382   {
383     return StringUtils.chomp(url, "/");
384   }
385 
386   /**
387    * Returns the URL to the relative target.
388    *
389    * @param target the relative URL.
390    * @return the absolute URL to the report or <code>null</code> if no root URL
391    *         is provided by the context.
392    * @throws IllegalArgumentException of {@code target} is blank.
393    */
394   public String createReportUrl(final String target)
395     throws IllegalArgumentException
396   {
397     Arguments.checkNotBlank("target", target);
398 
399     if (StringUtils.isEmpty(propertiesReportUrl))
400     {
401       return null;
402     }
403 
404     final String htmlFile = target + ".html";
405     if (target.charAt(0) == '/')
406     {
407       return propertiesReportUrl + htmlFile;
408     }
409     else
410     {
411       return propertiesReportUrl + '/' + htmlFile;
412     }
413   }
414 
415   /**
416    * Returns the URL to the report documentation for the given descriptor.
417    *
418    * @param descriptor the properties descriptor whose report documentation URL
419    *          is requested.
420    * @return the absolute URL to the report.
421    */
422   public String createReportUrl(final PropertyDescriptor descriptor)
423   {
424     final String target = resolve(descriptor);
425     final String url = createReportUrl(target);
426     return url;
427   }
428 
429   private static String resolve(final PropertyDescriptor descriptor)
430   {
431     final String target = descriptor.getKey().toString();
432     return target;
433   }
434 
435   /**
436    * Returns the URL to the XML report in the META-INF directory of the given
437    * descriptor.
438    *
439    * @param descriptor the properties descriptor whose XML report URL is
440    *          requested.
441    * @return the class loader root rooted path.
442    */
443   public String createMetaInfPath(final PropertyDescriptor descriptor)
444   {
445     return createMetaInfPath(descriptor, null);
446   }
447 
448   /**
449    * Returns the URL to the XML report in the META-INF directory of the given
450    * descriptor.
451    *
452    * @param descriptor the properties descriptor whose XML report URL is
453    *          requested.
454    * @param locale the locale to determine the comments.
455    * @return the class loader root rooted path.
456    */
457   @SuppressWarnings("unchecked")
458   public String createMetaInfPath(final PropertyDescriptor descriptor,
459       final Locale locale)
460   {
461     final String target = resolve(descriptor);
462 
463     if (locale != null)
464     {
465       final List<Locale> locales = LocaleUtils.localeLookupList(locale);
466       for (final Locale currentLocale : locales)
467       {
468         final String path =
469             META_INF_PROPERTY_REPORT + target + '_' + currentLocale + ".xml";
470         final ClassLoader loader = descriptor.getClass().getClassLoader(); // NOPMD
471         final URL resource = loader.getResource(path);
472         if (resource != null)
473         {
474           return path;
475         }
476       }
477     }
478 
479     final String path = META_INF_PROPERTY_REPORT + target + ".xml";
480     return path;
481   }
482 
483   /**
484    * Resolves the alias to the target it points to.
485    *
486    * @param alias the alias whose physical resource is requested.
487    * @return the target the alias points to.
488    * @throws BlankArgumentException if {@code alias} is blank.
489    * @throws UnknownAliasException if the alias is not known.
490    */
491   public String resolve(final String alias) throws BlankArgumentException,
492     UnknownAliasException
493   {
494     return aliasMapping.get(alias);
495   }
496 
497   /**
498    * Traverses the registered aliases.
499    *
500    * @param traverser the traverser to use.
501    * @throws NullPointerException if {@code traverser} is <code>null</code>.
502    */
503   public void traverseAliases(final AliasTraverser traverser)
504     throws NullPointerException
505   {
506     aliasMapping.traverse(traverser);
507   }
508 
509   /**
510    * Checks whether any aliases are registered.
511    *
512    * @return <code>true</code> if at least one alias is registered,
513    *         <code>false</code> otherwise.
514    */
515   public boolean hasAliases()
516   {
517     return !aliasMapping.isEmpty();
518   }
519 
520   // --- object basics --------------------------------------------------------
521 
522   /**
523    * Returns the string representation of the object.
524    *
525    * @return the string representation of the object.
526    */
527   @Override
528   public String toString()
529   {
530     return ToStringBuilder.reflectionToString(this);
531   }
532 }