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 * @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 * @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 * @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 * @Author(@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 * @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 }