View Javadoc
1   /**
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 1999-2002 (C) Intalio, Inc. All Rights Reserved.
32   */
33  package org.exolab.javasource;
34  
35  import java.lang.reflect.Array;
36  import java.util.LinkedHashMap;
37  
38  /**
39   * JAnnotation represents a single annotation against a code element. The methods described on the
40   * JAnnotatedElement interface are used to associate JAnnotation's with various other objects in
41   * this package describing Java code elements.
42   * <p>
43   * The print method outputs annotations in various forms (as described in the Java Language
44   * Specification Third Edition) based on the methods called.
45   * <p>
46   * For "Marker Annotation", construct with the appropriate JAnnotationType.
47   * 
48   * <pre>
49   * JAnnotationType preliminaryType = new JAnnotationType("Preliminary");
50   * JAnnotation preliminary = new JAnnotation(preliminaryType);
51   * </pre>
52   * 
53   * outputs
54   * 
55   * <pre>
56   *   &#064;Preliminary()
57   * </pre>
58   * 
59   * For "Single Element Annotation", construct as above and call the setValue(value) method to set
60   * the value of the "value" element of the annotation type.
61   * 
62   * <pre>
63   * JAnnotationType copyrightType = new JAnnotationType("Copyright");
64   * JAnnotation copyright = new JAnnotation(copyrightType);
65   * copyright.setValue("\"2002 Yoyodyne Systems, Inc., All rights reserved.\"");
66   * </pre>
67   * 
68   * outputs
69   * 
70   * <pre>
71   *   &#064;Copyright("2002 Yoyodyne Propulsion Systems, Inc., All rights reserved.")
72   * </pre>
73   * 
74   * For "Normal Annotation," construct as above then call the appropriate setValue methods that
75   * accept an "elementName" parameter.
76   * 
77   * <pre>
78   * JAnnotationType requestType = new JAnnotationType("RequestForEnhancement");
79   * JAnnotation request = new JAnnotation(requestType);
80   * request.setElementValue("id", "2868724");
81   * request.setElementValue("synopsis", "\"Provide time-travel functionality\"");
82   * request.setElementValue("engineer", "\"Mr. Peabody\"");
83   * request.setElementValue("date", "\"4/1/2004\"");
84   * </pre>
85   * 
86   * outputs
87   * 
88   * <pre>
89   *   &#064;RequestForEnhancement(
90   *       id       = 2868724,
91   *       sysopsis = "Provide time-travel functionality",
92   *       engineer = "Mr. Peabody",
93   *       date     = "4/1/2004")
94   * </pre>
95   * 
96   * "Complex" annotations are also supported via the various setValue methods that take a JAnnotation
97   * object.
98   * 
99   * <pre>
100  * JAnnotationType nameType = new JAnnotationType("Name");
101  * JAnnotationType authorType = new JAnnotationType("Author");
102  * JAnnotation author = new JAnnotation(authorType);
103  * JAnnotation name = new JAnnotation(nameType);
104  * name.setElementValue("first", "\"Joe\"");
105  * name.setElementValue("last", "\"Hacker\"");
106  * author.setValue(name);
107  * </pre>
108  * 
109  * outputs
110  * 
111  * <pre>
112  *   &#064;Author(&#064;Name(
113  *       first = "Joe",
114  *       last  = "Hacker"))
115  * </pre>
116  * 
117  * Finally annotation elements whose types are arrays are supported via the setValue methods that
118  * take arrays:
119  * 
120  * <pre>
121  * JAnnotationType endorsersType = new JAnnotationType("Endorsers");
122  * JAnnotation endorsers = new JAnnotation(endorsersType);
123  * endorsers.setValue(new String[] {"\"Children\"", "\"Unscrupulous dentists\""});
124  * </pre>
125  * 
126  * outputs
127  * 
128  * <pre>
129  *   &#064;Endorsers(
130  *       {
131  *           "Children",
132  *           "Unscrupulous dentists"
133  *       })
134  * </pre>
135  * 
136  * Note: Conditional element values are not currently supported. However the setValue methods taking
137  * String values can be used to output this construct literally if desired.
138  *
139  * @author <a href="mailto:andrew DOT fawcett AT coda DOTcom">Andrew Fawcett</a>
140  * @version $Revision$ $Date: 2006-04-25 16:09:10 -0600 (Tue, 25 Apr 2006) $
141  */
142 public final class JAnnotation {
143 
144   /**
145    * Name of a single element.
146    */
147   public static final String VALUE = "value";
148 
149   /**
150    * Annotation type referenced by this annotation.
151    */
152   private JAnnotationType _annotationType;
153 
154   /**
155    * Element values associated with this JAnnotation, contains String, String[], JAnnotation and
156    * JAnnotation[] objects.
157    */
158   private LinkedHashMap<Object, Object> _elementValues = new LinkedHashMap<Object, Object>();
159 
160   /**
161    * Constructs a JAnnotation for the given annotation type.
162    *
163    * @param annotationType Annotation type.
164    */
165   public JAnnotation(final JAnnotationType annotationType) {
166     _annotationType = annotationType;
167   }
168 
169   /**
170    * Returns the JAnnotationType associated with this JAnnotation.
171    *
172    * @return The JAnnotationType associated with this JAnnotation..
173    */
174   public JAnnotationType getAnnotationType() {
175     return _annotationType;
176   }
177 
178   /**
179    * Sets the "value" annotation element value.
180    *
181    * @param stringValue Literal String value.
182    */
183   public void setValue(final String stringValue) {
184     _elementValues.put(VALUE, stringValue);
185   }
186 
187   /**
188    * Sets the "value" annotation element value as a list.
189    *
190    * @param stringValue Array of literal String values.
191    */
192   public void setValue(final String[] stringValue) {
193     _elementValues.put(VALUE, stringValue);
194   }
195 
196   /**
197    * Sets the "value" annotation element value as an annotation.
198    *
199    * @param annotationValue JAnnotation to be used as this JAnnotation's value.
200    */
201   public void setValue(final JAnnotation annotationValue) {
202     _elementValues.put(VALUE, annotationValue);
203   }
204 
205   /**
206    * Sets the "value" annotation element value as a list of annotation values.
207    *
208    * @param annotationValues Array of JAnnotations to be used as this JAnnotation's value.
209    */
210   public void setValue(final JAnnotation[] annotationValues) {
211     _elementValues.put(VALUE, annotationValues);
212   }
213 
214   /**
215    * Adds an annotation element name=value pair.
216    *
217    * @param elementName Name of this annotation element.
218    * @param stringValue Value of this annotation element.
219    */
220   public void setElementValue(final String elementName, final String stringValue) {
221     _elementValues.put(elementName, stringValue);
222   }
223 
224   /**
225    * Adds an annotation element name=list pair.
226    *
227    * @param elementName Name of this annotation element.
228    * @param stringValues String array value of this annotation element.
229    */
230   public void setElementValue(final String elementName, final String[] stringValues) {
231     _elementValues.put(elementName, stringValues);
232   }
233 
234   /**
235    * Adds an annotation element name=annotation pair.
236    *
237    * @param elementName Name of this annotation element.
238    * @param annotationValue Annotation to be used as the value.
239    */
240   public void setElementValue(final String elementName, final JAnnotation annotationValue) {
241     _elementValues.put(elementName, annotationValue);
242   }
243 
244   /**
245    * Adds an annotation element name=array of annotations.
246    *
247    * @param elementName Name of this annotation element.
248    * @param annotationValues Array of annotations to be used as the value.
249    */
250   public void setElementValue(final String elementName, final JAnnotation[] annotationValues) {
251     _elementValues.put(elementName, annotationValues);
252   }
253 
254   /**
255    * Returns the annotation element value when it is a String.
256    *
257    * @return The annotation element value.
258    */
259   public String getValue() {
260     Object elementValue = getElementValueObject(VALUE);
261     if (elementValue instanceof String) {
262       return (String) elementValue;
263     }
264     throw new IllegalStateException("'value' element is not of type String.");
265   }
266 
267   /**
268    * Returns the annotation element value when it is an annotation.
269    *
270    * @return The annotation element value when it is an annotation.
271    */
272   public JAnnotation getValueAnnotation() {
273     Object elementValue = getElementValueObject(VALUE);
274     if (elementValue instanceof JAnnotation) {
275       return (JAnnotation) elementValue;
276     }
277     throw new IllegalStateException("'value' element is not of type JAnnotation.");
278   }
279 
280   /**
281    * For the provided element name, returns the annotation element value when it is a String.
282    *
283    * @param elementName Element to return the value of.
284    * @return The annotation element value.
285    */
286   public String getElementValue(final String elementName) {
287     Object elementValue = getElementValueObject(elementName);
288     if (elementValue instanceof String) {
289       return (String) elementValue;
290     }
291     throw new IllegalStateException("'" + elementName + "' element is not of type String.");
292   }
293 
294   /**
295    * For the provided element name, returns the annotation element value when it is an array of
296    * String.
297    *
298    * @param elementName Element to return the value of.
299    * @return The annotation element value.
300    */
301   public String[] getElementValueList(final String elementName) {
302     Object elementValue = getElementValueObject(elementName);
303     if (elementValue instanceof String[]) {
304       return (String[]) elementValue;
305     }
306     throw new IllegalStateException("'" + elementName + "' element is not of type String[].");
307   }
308 
309   /**
310    * Returns the given annotation element value as Object, typically used if the value type is not
311    * known. This will either be a String or JAnnotation or an array of String or an array of
312    * JAnnotation.
313    *
314    * @param elementName Element to return the value of.
315    * @return Annotation element value as Object.
316    */
317   public Object getElementValueObject(final String elementName) {
318     return _elementValues.get(elementName);
319   }
320 
321   /**
322    * For the provided element name, returns the annotation element value when it is a JAnnotation.
323    *
324    * @param elementName Element to return the value of.
325    * @return The annotation element value.
326    */
327   public JAnnotation getElementValueAnnotation(final String elementName) {
328     Object elementValue = getElementValueObject(elementName);
329     if (elementValue instanceof JAnnotation) {
330       return (JAnnotation) elementValue;
331     }
332     throw new IllegalStateException("'" + elementName + "' element is not of type JAnnotation.");
333   }
334 
335   /**
336    * For the provided element name, returns the annotation element value when it is an array of
337    * JAnnotation.
338    *
339    * @param elementName Element to return the value of.
340    * @return The annotation element value.
341    */
342   public JAnnotation[] getElementValueAnnotationList(final String elementName) {
343     Object elementValue = getElementValueObject(elementName);
344     if (elementValue instanceof JAnnotation[]) {
345       return (JAnnotation[]) elementValue;
346     }
347     throw new IllegalStateException("'" + elementName + "' element is not of type JAnnotation[].");
348   }
349 
350   /**
351    * Returns the names of the elements set by this annotation.
352    *
353    * @return Array of element names.
354    */
355   public String[] getElementNames() {
356     return _elementValues.keySet().toArray(new String[_elementValues.size()]);
357   }
358 
359   /**
360    * Prints the source code for this JAnnotation to the given JSourceWriter.
361    *
362    * @param jsw the JSourceWriter to print to. Must not be null.
363    */
364   public void print(final JSourceWriter jsw) {
365     jsw.write("@");
366     jsw.write(_annotationType.getLocalName());
367     jsw.write("(");
368     // Single element annotation?
369     String[] elementNames = getElementNames();
370     if (elementNames.length == 1 && elementNames[0].equals(VALUE)) {
371       // Just output value
372       printElementValue(jsw, getElementValueObject(VALUE));
373     } else if (elementNames.length > 0) {
374       // Max element name length?
375       int maxLength = 0;
376       for (String elementName : elementNames) {
377         int elementNameLength = elementName.length();
378         if (elementNameLength > maxLength) {
379           maxLength = elementNameLength;
380         }
381       }
382       // Output element name and values
383       jsw.writeln();
384       jsw.indent();
385       for (int i = 0; i < elementNames.length; i++) {
386         int elementNameLength = elementNames[i].length();
387         // Output element name with padding
388         jsw.write(elementNames[i]);
389         for (int p = 0; p < maxLength - elementNameLength; p++) {
390           jsw.write(" ");
391         }
392         // Assignment operator
393         jsw.write(" = ");
394         // Value
395         printElementValue(jsw, getElementValueObject(elementNames[i]));
396         if (i < elementNames.length - 1) {
397           jsw.write(",");
398           jsw.writeln();
399         }
400       }
401       jsw.unindent();
402     }
403     jsw.write(")");
404   }
405 
406   /**
407    * Prints annotation element value according to its type: String, String[], JAnnotation or
408    * JAnnotation[].
409    *
410    * @param jsw the JSourceWriter to print to. Must not be null.
411    * @param elementValue element value to print
412    */
413   private void printElementValue(final JSourceWriter jsw, final Object elementValue) {
414     // String?
415     if (elementValue instanceof String) {
416       jsw.write((String) elementValue);
417       return;
418     } else if (elementValue instanceof JAnnotation) {
419       JAnnotation annotation = (JAnnotation) elementValue;
420       annotation.print(jsw);
421       return;
422     } else if (elementValue.getClass().isArray()) {
423       // Short hand for single item list
424       int listLength = Array.getLength(elementValue);
425       if (listLength == 1) {
426         printElementValue(jsw, Array.get(elementValue, 0));
427         return;
428       }
429       // Output list items
430       jsw.indent();
431       jsw.writeln();
432       jsw.write("{");
433       jsw.writeln();
434       jsw.indent();
435       for (int i = 0; i < listLength; i++) {
436         printElementValue(jsw, Array.get(elementValue, i));
437         if (i < listLength - 1) {
438           jsw.write(",");
439         }
440         jsw.writeln();
441       }
442       jsw.unindent();
443       jsw.write("}");
444       jsw.unindent();
445       return;
446     }
447     throw new IllegalArgumentException("'" + elementValue + "' was not expected.");
448   }
449 
450 }