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.spi.core.metadata;
17  
18  import static de.smartics.util.lang.StaticAnalysis.UNCHECKED;
19  
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Locale;
23  
24  import org.apache.commons.lang.builder.ToStringBuilder;
25  
26  import de.smartics.properties.api.core.annotations.AccessType;
27  import de.smartics.properties.api.core.annotations.PropertyDefinitionTime;
28  import de.smartics.properties.api.core.annotations.PropertyUse.UseType;
29  import de.smartics.properties.api.core.domain.DocumentName;
30  import de.smartics.properties.api.core.domain.PropertyCategories;
31  import de.smartics.properties.api.core.domain.PropertyComment;
32  import de.smartics.properties.api.core.domain.PropertyConstraint;
33  import de.smartics.properties.api.core.domain.PropertyContext;
34  import de.smartics.properties.api.core.domain.PropertyDescriptor;
35  import de.smartics.properties.api.core.domain.PropertyExpression;
36  import de.smartics.properties.api.core.domain.PropertyKey;
37  import de.smartics.properties.api.core.domain.PropertyProjectdoc;
38  import de.smartics.properties.api.core.domain.PropertySetProjectdoc;
39  import de.smartics.properties.api.core.domain.PropertyType;
40  import de.smartics.properties.api.core.domain.PropertyValueRange;
41  import de.smartics.properties.spi.core.context.PropertyContextProvider;
42  import de.smartics.util.lang.Arg;
43  
44  /**
45   * An implementation of the property descriptor interface that allows to set
46   * property meta data.
47   */
48  public final class PropertyMetaData implements PropertyDescriptor
49  { // NOPMD
50    // ********************************* Fields *********************************
51  
52    // --- constants ------------------------------------------------------------
53  
54    /**
55     * The class version identifier.
56     */
57    private static final long serialVersionUID = 1L;
58  
59    // --- members --------------------------------------------------------------
60  
61    /**
62     * The context of the property descriptor.
63     *
64     * @serial
65     */
66    private final PropertyContextProvider contextProxy;
67  
68    /**
69     * The type that declares the property that is described by this instance.
70     *
71     * @serial
72     */
73    private final Class<?> declaringType;
74  
75    /**
76     * The key of the property. Must not be <code>null</code>.
77     *
78     * @serial
79     */
80    private final PropertyKey key;
81  
82    /**
83     * The type of the property value. Must not be <code>null</code>.
84     *
85     * @serial
86     */
87    private final PropertyType type;
88  
89    /**
90     * The flag to determine whether or not the property value is mandatory to be
91     * specified. A property has a value if it is either specified explicitly or
92     * derived from the property expression.
93     *
94     * @serial
95     */
96    private final boolean mandatory;
97  
98    /**
99     * The flag to determine whether or not the property value contains sensitive
100    * information that is required to be secured.
101    *
102    * @serial
103    */
104   private final boolean secured;
105 
106   /**
107    * The flag to define the access type.
108    */
109   private final AccessType accessType;
110 
111   /**
112    * The update interval in milliseconds (ms).
113    * <p>
114    * An application may cache the value of the property for the given period of
115    * time.
116    * </p>
117    * <table>
118    * <tr>
119    * <th>value</th>
120    * <th>description</th>
121    * </tr>
122    * <tr>
123    * <td>&lt; 0</td>
124    * <td>Property is not mutable.</td>
125    * </tr>
126    * <tr>
127    * <td>0</td>
128    * <td>Property is required on each access.</td>
129    * </tr>
130    * <tr>
131    * <td>&gt; 0</td>
132    * <td>Property is required to be read new after the given number of ms
133    * elapsed.</td>
134    * </tr>
135    * </table>
136    *
137    * @serial
138    */
139   private final long updateIntervalInMs;
140 
141   /**
142    * The designed time the property is defined.
143    *
144    * @serial
145    */
146   private final PropertyDefinitionTime configurationTime;
147 
148   /**
149    * The default expression to evaluate a default property value. May be
150    * <code>null</code>.
151    *
152    * @serial
153    */
154   private final PropertyExpression defaultExpression;
155 
156   /**
157    * The range of values allowed for this property.
158    *
159    * @serial
160    */
161   private final PropertyValueRange<?> valueRange;
162 
163   /**
164    * The list of constraints a value for the property has to meet. May be empty,
165    * but is never <code>null</code>.
166    *
167    * @serial
168    */
169   private final List<? extends PropertyConstraint<?>> constraints;
170 
171   /**
172    * The meta data information for the property.
173    *
174    * @serial
175    */
176   private final DocumentMetaDataProxy documentMetaDataProxy;
177 
178   /**
179    * The comment to the property and its values.
180    *
181    * @serial
182    */
183   private final PropertyCommentProvider commentProxy;
184 
185   /**
186    * The categories the property is associated with.
187    *
188    * @serial
189    */
190   private final PropertyCategories categories;
191 
192   /**
193    * The use type of the property.
194    *
195    * @serial
196    */
197   private final UseType useType;
198 
199   // ****************************** Initializer *******************************
200 
201   // ****************************** Constructors ******************************
202 
203   @SuppressWarnings(UNCHECKED)
204   private PropertyMetaData(final Builder builder)
205   {
206     this.contextProxy = builder.context;
207     this.declaringType = builder.declaringType;
208     this.key = builder.key;
209     this.type = builder.type;
210 
211     this.mandatory = builder.mandatory;
212     this.secured = builder.secured;
213     this.accessType = builder.accessType;
214     this.updateIntervalInMs = builder.updateIntervalInMs;
215     this.configurationTime = builder.configurationTime;
216 
217     this.defaultExpression = builder.defaultExpression;
218     this.valueRange = builder.valueRange;
219     this.constraints = builder.constraints;
220 
221     this.documentMetaDataProxy = builder.documentMetaDataProxy;
222     this.commentProxy = builder.commentProxy;
223     this.categories = builder.categories;
224     this.useType = builder.useType;
225   }
226 
227   // ****************************** Inner Classes *****************************
228 
229   /**
230    * Creates instances of type {@link PropertyMetaData}.
231    */
232   public static final class Builder
233   { // NOPMD
234     // ******************************** Fields ********************************
235 
236     // --- constants ----------------------------------------------------------
237 
238     // --- members ------------------------------------------------------------
239 
240     /**
241      * The context of the property descriptor.
242      */
243     private PropertyContextProvider context;
244 
245     /**
246      * The type that declares the property that is described by this instance.
247      */
248     private Class<?> declaringType;
249 
250     /**
251      * The key of the property. Must not be <code>null</code>.
252      */
253     private PropertyKey key;
254 
255     /**
256      * The type of the property value. Must not be <code>null</code>.
257      */
258     private PropertyType type;
259 
260     /**
261      * The flag to determine whether or not the property value is mandatory to
262      * be specified. A property has a value if it is either specified explicitly
263      * or derived from the property expression.
264      */
265     private boolean mandatory;
266 
267     /**
268      * The flag to determine whether or not the property value contains
269      * sensitive information that is required to be secured.
270      */
271     private boolean secured;
272 
273     /**
274      * The flag to define the access type.
275      *
276      * @impl The default value is used, if the annotation is not specified. Make
277      *       sure that this default is in-sync with
278      *       {@link de.smartics.properties.api.core.annotations.PropertyLifecycle#access()}
279      *       .
280      */
281     private AccessType accessType = AccessType.READ_ONLY;
282 
283     /**
284      * The update interval in milliseconds (ms).
285      * <p>
286      * An application may cache the value of the property for the given period
287      * of time.
288      * </p>
289      * <table>
290      * <tr>
291      * <th>value</th>
292      * <th>description</th>
293      * </tr>
294      * <tr>
295      * <td>&lt; 0</td>
296      * <td>Property is not mutable.</td>
297      * </tr>
298      * <tr>
299      * <td>0</td>
300      * <td>Property is required on each access.</td>
301      * </tr>
302      * <tr>
303      * <td>&gt; 0</td>
304      * <td>Property is required to be read new after the given number of ms
305      * elapsed.</td>
306      * </tr>
307      * </table>
308      *
309      * @impl The default value is used, if the annotation is not specified. Make
310      *       sure that this default is in-sync with
311      *       {@link de.smartics.properties.api.core.annotations.PropertyLifecycle#updateInterval()}
312      *       .
313      */
314     private long updateIntervalInMs = -1;
315 
316     /**
317      * The designed time the property is defined.
318      *
319      * @impl The default value is used, if the annotation is not specified. Make
320      *       sure that this default is in-sync with
321      *       {@link de.smartics.properties.api.core.annotations.PropertyLifecycle#configurationTime}
322      *       .
323      */
324     private PropertyDefinitionTime configurationTime =
325         PropertyDefinitionTime.STARTUP;
326 
327     /**
328      * The default expression to evaluate a default property value. May be
329      * <code>null</code>.
330      */
331     private PropertyExpression defaultExpression =
332         PropertyExpression.NO_EXPRESSION;
333 
334     /**
335      * The range of values allowed for this property.
336      */
337     private PropertyValueRange<?> valueRange;
338 
339     /**
340      * The list of constraints a value for the property has to meet. May be
341      * empty, but is never <code>null</code>.
342      */
343     @SuppressWarnings("rawtypes")
344     private final List constraints = new ArrayList<PropertyConstraint<?>>();
345 
346     /**
347      * The meta data information for the property.
348      */
349     private DocumentMetaDataProxy documentMetaDataProxy;
350 
351     /**
352      * The comment to the property and its values.
353      */
354     private PropertyCommentProvider commentProxy;
355 
356     /**
357      * The categories the property is associated with.
358      */
359     private PropertyCategories categories;
360 
361     /**
362      * The use type of the property.
363      */
364     private UseType useType;
365 
366     // ***************************** Initializer ******************************
367 
368     // ***************************** Constructors *****************************
369 
370     // ***************************** Inner Classes ****************************
371 
372     // ******************************** Methods *******************************
373 
374     // --- init ---------------------------------------------------------------
375 
376     // --- get&set ------------------------------------------------------------
377 
378     /**
379      * Returns the key of the property.
380      *
381      * @return the key of the property. May be <code>null</code> if not yet set
382      *         to the builder.
383      */
384     public PropertyKey getKey()
385     {
386       return key;
387     }
388 
389     /**
390      * Returns the type of the property value.
391      *
392      * @return the type of the property value. May be <code>null</code> if not
393      *         yet set to the builder.
394      */
395     public PropertyType getType()
396     {
397       return type;
398     }
399 
400     // --- business -----------------------------------------------------------
401 
402     /**
403      * Sets the context of the property descriptor.
404      *
405      * @param context the context of the property descriptor.
406      * @return the builder instance.
407      * @throws NullPointerException if {@code context} is <code>null</code>.
408      */
409     public Builder with(final PropertyContextProvider context)
410       throws NullPointerException
411     {
412       this.context = Arg.checkNotNull("context", context);
413       return this;
414     }
415 
416     /**
417      * Sets the flag to determine whether or not the property value contains
418      * sensitive information that is required to be secured.
419      *
420      * @param secured the flag to determine whether or not the property value
421      *          contains sensitive information that is required to be secured.
422      */
423     public void setSecured(final boolean secured)
424     {
425       this.secured = secured;
426     }
427 
428     /**
429      * Sets the type that declares the property that is described by this
430      * instance.
431      *
432      * @param declaringType the type that declares the property that is
433      *          described by this instance.
434      * @return the builder instance.
435      * @throws NullPointerException if {@code declaringType} is
436      *           <code>null</code>.
437      */
438     public Builder withDeclaringType(final Class<?> declaringType)
439       throws NullPointerException
440     {
441       this.declaringType = Arg.checkNotNull("declaringType", declaringType);
442       return this;
443     }
444 
445     /**
446      * Sets the key of the property. Must not be <code>null</code>.
447      *
448      * @param key the key of the property.
449      * @return the builder instance.
450      * @throws NullPointerException if {@code key} is <code>null</code>.
451      */
452     public Builder with(final PropertyKey key) throws NullPointerException
453     {
454       this.key = Arg.checkNotNull("key", key);
455       return this;
456     }
457 
458     /**
459      * Sets the type of the property value. Must not be <code>null</code>.
460      *
461      * @param type the type of the property value.
462      * @return the builder instance.
463      * @throws NullPointerException if {@code type} is <code>null</code>.
464      */
465     public Builder with(final PropertyType type) throws NullPointerException
466     {
467       this.type = Arg.checkNotNull("type", type);
468       return this;
469     }
470 
471     /**
472      * Sets the flag to define the access type.
473      *
474      * @param accessType the flag to define the access type.
475      * @return the builder instance.
476      * @throws NullPointerException if {@code accessType} is <code>null</code>.
477      */
478     public Builder with(final AccessType accessType)
479       throws NullPointerException
480     {
481       this.accessType = Arg.checkNotNull("accessType", accessType);
482       return this;
483     }
484 
485     /**
486      * Sets the update interval in milliseconds (ms).
487      * <p>
488      * An application may cache the value of the property for the given period
489      * of time.
490      * </p>
491      * <table>
492      * <tr>
493      * <th>value</th>
494      * <th>description</th>
495      * </tr>
496      * <tr>
497      * <td>&lt; 0</td>
498      * <td>Property is not mutable.</td>
499      * </tr>
500      * <tr>
501      * <td>0</td>
502      * <td>Property is required on each access.</td>
503      * </tr>
504      * <tr>
505      * <td>&gt; 0</td>
506      * <td>Property is required to be read new after the given number of ms
507      * elapsed.</td>
508      * </tr>
509      * </table>
510      *
511      * @param updateIntervalInMs the update interval in milliseconds (ms).
512      * @return the builder instance.
513      */
514     public Builder withUpdateIntervalInMs(final long updateIntervalInMs)
515     {
516       this.updateIntervalInMs = updateIntervalInMs;
517       return this;
518     }
519 
520     /**
521      * Sets the designed time the property is defined.
522      *
523      * @param configurationTime the designed time the property is defined.
524      * @return the builder instance.
525      * @throws NullPointerException if {@code defaultExpression} is
526      *           <code>null</code>.
527      */
528     public Builder with(final PropertyDefinitionTime configurationTime)
529       throws NullPointerException
530     {
531       this.configurationTime =
532           Arg.checkNotNull("configurationTime", configurationTime);
533       return this;
534     }
535 
536     /**
537      * Sets the default expression to evaluate a default property value. May be
538      * <code>null</code>.
539      *
540      * @param defaultExpression the default expression to evaluate a default
541      *          property value.
542      * @return the builder instance.
543      * @throws NullPointerException if {@code defaultExpression} is
544      *           <code>null</code>.
545      */
546     public Builder with(final PropertyExpression defaultExpression)
547       throws NullPointerException
548     {
549       this.defaultExpression =
550           Arg.checkNotNull("defaultExpression", defaultExpression);
551       return this;
552     }
553 
554     /**
555      * Sets the range of values allowed for this property.
556      *
557      * @param valueRange the range of values allowed for this property.
558      * @return the builder instance.
559      */
560     public Builder with(final PropertyValueRange<?> valueRange)
561     {
562       this.valueRange = valueRange;
563       return this;
564     }
565 
566     /**
567      * Sets the meta data information for the property.
568      *
569      * @param documentMetaData the meta data information for the property.
570      * @return the builder instance.
571      * @throws NullPointerException if {@code documentMetaData} is
572      *           <code>null</code>.
573      */
574     public Builder with(final DocumentMetaDataProxy documentMetaData)
575       throws NullPointerException
576     {
577       this.documentMetaDataProxy =
578           Arg.checkNotNull("documentMetaData", documentMetaData);
579       return this;
580     }
581 
582     /**
583      * Sets the comment proxy to the property and its values.
584      *
585      * @param commentProxy the comment proxy to the property and its values.
586      * @return the builder instance.
587      * @throws NullPointerException if {@code commentProxy} is <code>null</code>
588      *           .
589      */
590     public Builder with(final PropertyCommentProvider commentProxy)
591       throws NullPointerException
592     {
593       this.commentProxy = Arg.checkNotNull("commentProxy", commentProxy);
594       return this;
595     }
596 
597     /**
598      * Adds the given constraint to the list of constraints.
599      *
600      * @param constraint the constraint to add.
601      * @throws NullPointerException if {@code constraint} is <code>null</code>.
602      */
603     @SuppressWarnings(UNCHECKED)
604     public void add(final PropertyConstraint<?> constraint)
605       throws NullPointerException
606     {
607       Arg.checkNotNull("constraint", constraint);
608       this.constraints.add(constraint);
609     }
610 
611     /**
612      * Sets the categories the property is associated with.
613      *
614      * @param categories the categories the property is associated with.
615      * @return the builder instance.
616      * @throws NullPointerException if {@code categories} is <code>null</code>.
617      */
618     public Builder with(final PropertyCategories categories)
619       throws NullPointerException
620     {
621       this.categories = Arg.checkNotNull("categories", categories);
622       return this;
623     }
624 
625     /**
626      * Sets the use type of the property.
627      *
628      * @param useType the use type of the property.
629      * @return the builder instance.
630      * @throws NullPointerException if {@code useType} is <code>null</code>.
631      */
632     public Builder with(final UseType useType) throws NullPointerException
633     {
634       this.useType = Arg.checkNotNull("useType", useType);
635       return this;
636     }
637 
638     /**
639      * Creates the instance.
640      *
641      * @return the created instance.
642      * @throws NullPointerException if any of the required arguments is not set.
643      */
644     @SuppressWarnings(UNCHECKED)
645     public PropertyMetaData build() throws NullPointerException
646     {
647       Arg.checkNotNull("context", context);
648       Arg.checkNotNull("declaringType", declaringType);
649       Arg.checkNotNull("key", key);
650       Arg.checkNotNull("type", type);
651       Arg.checkNotNull("accessType", accessType);
652       Arg.checkNotNull("configurationTime", configurationTime);
653       Arg.checkNotNull("defaultExpression", defaultExpression);
654       Arg.checkNotNull("constraints", constraints);
655       Arg.checkNotNull("documentMetaData", documentMetaDataProxy);
656       Arg.checkNotNull("commentProxy", commentProxy);
657 
658       mandatory = containsMandatoryConstaint(constraints);
659 
660       if (categories == null)
661       {
662         categories = new PropertyCategories.Builder().build();
663       }
664       if (useType == null)
665       {
666         useType = UseType.CONFIGURATION;
667       }
668 
669       return new PropertyMetaData(this);
670     }
671 
672     private static boolean containsMandatoryConstaint(
673         final List<? extends PropertyConstraint<?>> constraints)
674     {
675       for (final PropertyConstraint<?> constraint : constraints)
676       {
677         if (constraint.isMandatoryConstraint())
678         {
679           return true;
680         }
681       }
682       return false;
683     }
684 
685     // --- object basics ------------------------------------------------------
686   }
687 
688   // ********************************* Methods ********************************
689 
690   // --- init -----------------------------------------------------------------
691 
692   // --- get&set --------------------------------------------------------------
693 
694   @Override
695   public PropertyContext getContext()
696   {
697     return contextProxy.getPropertyContext(this);
698   }
699 
700   @Override
701   public Class<?> getDeclaringType()
702   {
703     return declaringType;
704   }
705 
706   @Override
707   public PropertyKey getKey()
708   {
709     return key;
710   }
711 
712   @Override
713   public PropertyType getType()
714   {
715     return type;
716   }
717 
718   @Override
719   public boolean isMandatory()
720   {
721     return mandatory;
722   }
723 
724   /**
725    * Returns the flag to determine whether or not the property value contains
726    * sensitive information that is required to be secured.
727    *
728    * @return the flag to determine whether or not the property value contains
729    *         sensitive information that is required to be secured.
730    */
731   @Override
732   public boolean isSecured()
733   {
734     return secured;
735   }
736 
737   @Override
738   public AccessType getAccessType()
739   {
740     return accessType;
741   }
742 
743   @Override
744   public boolean isRuntimeMutable()
745   {
746     return accessType == AccessType.READ_WRITE;
747   }
748 
749   @Override
750   public long getUpdateIntervalInMs()
751   {
752     return updateIntervalInMs;
753   }
754 
755   @Override
756   public PropertyDefinitionTime getConfigurationTime()
757   {
758     return configurationTime;
759   }
760 
761   @Override
762   public PropertyExpression getDefaultExpression()
763   {
764     return defaultExpression;
765   }
766 
767   @Override
768   public PropertyValueRange<?> getValueRange()
769   {
770     return valueRange;
771   }
772 
773   @Override
774   public List<? extends PropertyConstraint<?>> getConstraints()
775   {
776     return constraints;
777   }
778 
779   @Override
780   public DocumentName getDocumentName()
781   {
782     return documentMetaDataProxy.getDocumentName();
783   }
784 
785   @Override
786   public PropertyProjectdoc getDocumentMetaData()
787   {
788     return getDocumentMetaData(null);
789   }
790 
791   @Override
792   public PropertyProjectdoc getDocumentMetaData(final Locale locale)
793   {
794     return documentMetaDataProxy.getProjectdocProperty(this, locale);
795   }
796 
797   @Override
798   public PropertySetProjectdoc getDocumentMetaDataProjectSet()
799   {
800     return getDocumentMetaDataProjectSet(null);
801   }
802 
803   @Override
804   public PropertySetProjectdoc getDocumentMetaDataProjectSet(final Locale locale)
805   {
806     return documentMetaDataProxy.getProjectdocPropertySet(this, locale);
807   }
808 
809   @Override
810   public PropertyComment getComment()
811   {
812     return getComment(null);
813   }
814 
815   @Override
816   public PropertyComment getComment(final Locale locale)
817   {
818     return commentProxy.getComment(this, locale);
819   }
820 
821   @Override
822   public PropertyCategories getCategories()
823   {
824     return categories;
825   }
826 
827   /**
828    * Returns the use type of the property.
829    *
830    * @return the use type of the property.
831    */
832   @Override
833   public UseType getUseType()
834   {
835     return useType;
836   }
837 
838   // --- business -------------------------------------------------------------
839 
840   // --- object basics --------------------------------------------------------
841 
842   /**
843    * Returns the string representation of the object.
844    *
845    * @return the string representation of the object.
846    */
847   @Override
848   public String toString()
849   {
850     return ToStringBuilder.reflectionToString(this);
851   }
852 }