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