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 java.lang.reflect.Method;
19  import java.util.HashMap;
20  import java.util.Locale;
21  import java.util.Map;
22  
23  import javax.annotation.concurrent.ThreadSafe;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.apache.commons.lang.builder.ToStringBuilder;
27  
28  import de.smartics.projectdoc.annotations.Document;
29  import de.smartics.properties.api.core.domain.DocumentName;
30  import de.smartics.properties.api.core.domain.PropertiesContext;
31  import de.smartics.properties.api.core.domain.PropertyDescriptor;
32  import de.smartics.properties.api.core.domain.PropertyKey;
33  import de.smartics.properties.api.core.domain.PropertyProjectdoc;
34  import de.smartics.properties.api.core.domain.PropertySetProjectdoc;
35  import de.smartics.properties.spi.core.metadata.projectdoc.AnnotationHelper;
36  import de.smartics.properties.spi.core.metadata.projectdoc.ProjectdocAnnotationCollector;
37  import de.smartics.properties.spi.core.metadata.projectdoc.ProjectdocAnnotationCollector.Defaults;
38  import de.smartics.properties.spi.core.metadata.projectdoc.PropertyProjectdocParser;
39  import de.smartics.properties.spi.core.metadata.projectdoc.PropertySetProjectdocParser;
40  import de.smartics.properties.spi.core.util.SerializableMethod;
41  import de.smartics.util.lang.Arg;
42  
43  /**
44   * Allows to lazy load annotation information.
45   */
46  @ThreadSafe
47  public final class MetaInfDocumentMetaDataProxy implements
48      DocumentMetaDataProxy
49  {
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 name of the document that identifies the document uniquely within the
63     * context of the project. It is used to select the projectdoc document in the
64     * <code>META-INF</code> folder.
65     *
66     * @serial
67     */
68    private final DocumentName documentName;
69  
70    /**
71     * The properties context to fetch information from the <code>META-INF</code>
72     * folder. May be <code>null</code>.
73     *
74     * @serial
75     */
76    private final PropertiesContext context;
77  
78    /**
79     * The method to use to fetch annotations in case the context is not to be
80     * used.
81     *
82     * @serial
83     */
84    private final SerializableMethod propertyMethod;
85  
86    /**
87     * The cached property comments.
88     *
89     * @serial
90     */
91    private final Map<Locale, PropertyProjectdoc> metaData =
92        new HashMap<Locale, PropertyProjectdoc>();
93  
94    /**
95     * The cached property set comments.
96     *
97     * @serial
98     */
99    private final Map<Locale, PropertySetProjectdoc> propertySetMetaData =
100       new HashMap<Locale, PropertySetProjectdoc>();
101 
102   // ****************************** Initializer *******************************
103 
104   // ****************************** Constructors ******************************
105 
106   /**
107    * Default constructor.
108    *
109    * @param key the property key to the method.
110    * @param context the properties context to fetch information from the
111    *          <code>META-INF</code> folder. May be <code>null</code>.
112    * @param propertyMethod the method to use to fetch annotations in case the
113    *          context is not to be used.
114    * @throws NullPointerException if {@code key} or {@code propertyMethod} is
115    *           {@code null}.
116    */
117   public MetaInfDocumentMetaDataProxy(final PropertyKey key,
118       final PropertiesContext context, final Method propertyMethod)
119     throws NullPointerException
120   {
121     Arg.checkNotNull("key", key);
122 
123     this.context = context;
124     this.propertyMethod =
125         new SerializableMethod(Arg.checkNotNull("propertyMethod",
126             propertyMethod));
127     this.documentName = calculateDocumentName(key, propertyMethod);
128   }
129 
130   // ****************************** Inner Classes *****************************
131 
132   // ********************************* Methods ********************************
133 
134   // --- init -----------------------------------------------------------------
135 
136   // --- get&set --------------------------------------------------------------
137 
138   private static DocumentName calculateDocumentName(final PropertyKey key,
139       final Method propertyMethod)
140   {
141     final Document annotation = propertyMethod.getAnnotation(Document.class);
142     String name = propertyMethod.getName();
143     if (annotation != null)
144     {
145       final String value = annotation.name();
146       if (value != null)
147       {
148         final AnnotationHelper helper = new AnnotationHelper();
149         final String annName = helper.normalizeString(value);
150         if (StringUtils.isNotBlank(annName)) // NOPMD
151         {
152           name = annName;
153         }
154       }
155     }
156 
157     final String setName = key.getPropertySet();
158     final String fqn = setName != null ? setName + '.' + name : name;
159     final DocumentName documentName = new DocumentName(fqn);
160     return documentName;
161   }
162 
163   /**
164    * Returns the name of the document that identifies the document uniquely
165    * within the context of the project. It is used to select the projectdoc
166    * document in the <code>META-INF</code> folder.
167    *
168    * @return the name of the document that identifies the document uniquely
169    *         within the context of the project.
170    */
171   @Override
172   public DocumentName getDocumentName()
173   {
174     return documentName;
175   }
176 
177   // --- business -------------------------------------------------------------
178 
179   @Override
180   public PropertySetProjectdoc getProjectdocPropertySet(
181       final PropertyDescriptor descriptor, final Locale locale)
182   {
183     synchronized (propertySetMetaData)
184     {
185       PropertySetProjectdoc metaData = propertySetMetaData.get(locale);
186 
187       if (metaData == null)
188       {
189         final Method propertyMethod = this.propertyMethod.getMethod();
190 
191         ensureMethodPresent(propertyMethod);
192 
193         if (context != null)
194         {
195           final Defaults defaults =
196               PropertyMetaDataDefaults.propertySet(propertyMethod);
197           final PropertySetProjectdocParser parser =
198               new PropertySetProjectdocParser(context, defaults);
199           metaData = parser.parse(descriptor, locale);
200         }
201 
202         if (metaData == null)
203         {
204           metaData = new PropertySetProjectdoc();
205         }
206 
207         this.propertySetMetaData.put(locale, metaData);
208       }
209 
210       return metaData;
211     }
212   }
213 
214   @Override
215   public PropertyProjectdoc getProjectdocProperty(
216       final PropertyDescriptor descriptor, final Locale locale)
217   {
218     synchronized (metaData)
219     {
220       PropertyProjectdoc metaData = this.metaData.get(locale);
221 
222       if (metaData == null)
223       {
224         final Method propertyMethod = this.propertyMethod.getMethod();
225 
226         ensureMethodPresent(propertyMethod);
227 
228         if (context != null)
229         {
230           final Defaults defaults =
231               new PropertyMetaDataDefaults(propertyMethod);
232           final PropertyProjectdocParser parser =
233               new PropertyProjectdocParser(context, defaults);
234           metaData = parser.parse(descriptor, locale);
235         }
236 
237         if (metaData == null)
238         {
239           final Class<?> declaringType = propertyMethod.getDeclaringClass();
240           final Defaults parentDefaults =
241               new PropertyMetaDataDefaults(declaringType, propertyMethod);
242           final Defaults defaults =
243               new PropertyMetaDataDefaults(propertyMethod);
244           final ProjectdocAnnotationCollector collector =
245               new ProjectdocAnnotationCollector(declaringType, parentDefaults,
246                   defaults);
247 
248           metaData =
249               PropertyProjectdoc.upgrade(collector
250                   .createMetaDataFromAnnotations(propertyMethod));
251         }
252 
253         if (metaData != null)
254         {
255           this.metaData.put(locale, metaData);
256         }
257       }
258 
259       return metaData;
260     }
261   }
262 
263   private void ensureMethodPresent(final Method propertyMethod)
264   {
265     if (propertyMethod == null)
266     {
267       throw new IllegalStateException(
268           "Cannot find documentation for method value 'null'.");
269     }
270   }
271 
272   // --- object basics --------------------------------------------------------
273 
274   /**
275    * Returns the string representation of the object.
276    *
277    * @return the string representation of the object.
278    */
279   @Override
280   public String toString()
281   {
282     return ToStringBuilder.reflectionToString(this);
283   }
284 }