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