001: /**
002: * Redistribution and use of this software and associated documentation
003: * ("Software"), with or without modification, are permitted provided
004: * that the following conditions are met:
005: *
006: * 1. Redistributions of source code must retain copyright
007: * statements and notices. Redistributions must also contain a
008: * copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the
011: * above copyright notice, this list of conditions and the
012: * following disclaimer in the documentation and/or other
013: * materials provided with the distribution.
014: *
015: * 3. The name "Exolab" must not be used to endorse or promote
016: * products derived from this Software without prior written
017: * permission of Intalio, Inc. For written permission,
018: * please contact info@exolab.org.
019: *
020: * 4. Products derived from this Software may not be called "Exolab"
021: * nor may "Exolab" appear in their names without prior written
022: * permission of Intalio, Inc. Exolab is a registered
023: * trademark of Intalio, Inc.
024: *
025: * 5. Due credit should be given to the Exolab Project
026: * (http://www.exolab.org/).
027: *
028: * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
029: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
030: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
031: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
033: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
035: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
036: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
037: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
038: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
039: * OF THE POSSIBILITY OF SUCH DAMAGE.
040: *
041: * Copyright 1999-2002 (C) Intalio, Inc. All Rights Reserved.
042: */package org.exolab.javasource;
043:
044: import java.lang.reflect.Array;
045:
046: import org.exolab.castor.util.OrderedHashMap;
047:
048: /**
049: * JAnnotation represents a single annotation against a code element. The
050: * methods described on the JAnnotatedElement interface are used to associate
051: * JAnnotation's with various other objects in this package describing Java code
052: * elements.
053: * <p>
054: * The print method outputs annotations in various forms (as described in the
055: * Java Language Specification Third Edition) based on the methods called.
056: * <p>
057: * For "Marker Annotation", construct with the appropriate JAnnotationType.
058: * <pre>
059: * JAnnotationType preliminaryType = new JAnnotationType("Preliminary");
060: * JAnnotation preliminary = new JAnnotation(preliminaryType);
061: * </pre>
062: * outputs
063: * <pre>
064: * @Preliminary()
065: * </pre>
066: * For "Single Element Annotation", construct as above and call the
067: * setValue(value) method to set the value of the "value" element of the
068: * annotation type.
069: * <pre>
070: * JAnnotationType copyrightType = new JAnnotationType("Copyright");
071: * JAnnotation copyright = new JAnnotation(copyrightType);
072: * copyright.setValue("\"2002 Yoyodyne Systems, Inc., All rights reserved.\"");
073: * </pre>
074: * outputs
075: * <pre>
076: * @Copyright("2002 Yoyodyne Propulsion Systems, Inc., All rights reserved.")
077: * </pre>
078: * For "Normal Annotation," construct as above then call the appropriate
079: * setValue methods that accept an "elementName" parameter.
080: * <pre>
081: * JAnnotationType requestType = new JAnnotationType("RequestForEnhancement");
082: * JAnnotation request = new JAnnotation(requestType);
083: * request.setElementValue("id", "2868724");
084: * request.setElementValue("synopsis", "\"Provide time-travel functionality\"");
085: * request.setElementValue("engineer", "\"Mr. Peabody\"");
086: * request.setElementValue("date", "\"4/1/2004\"");
087: * </pre>
088: * outputs
089: * <pre>
090: * @RequestForEnhancement(
091: * id = 2868724,
092: * sysopsis = "Provide time-travel functionality",
093: * engineer = "Mr. Peabody",
094: * date = "4/1/2004")
095: * </pre>
096: * "Complex" annotations are also supported via the various setValue methods
097: * that take a JAnnotation object.
098: * <pre>
099: * 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: 6669 $ $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: private static final String VALUE = "value";
140:
141: //--------------------------------------------------------------------------
142:
143: /** Annotation type referenced by this annotation. */
144: private JAnnotationType _annotationType;
145:
146: /** Element values associated with this JAnnotation, contains String,
147: * String[], JAnnotation and JAnnotation[] objects. */
148: private OrderedHashMap _elementValues = new OrderedHashMap();
149:
150: //--------------------------------------------------------------------------
151:
152: /**
153: * Constructs a JAnnotation for the given annotation type.
154: *
155: * @param annotationType Annotation type.
156: */
157: public JAnnotation(final JAnnotationType annotationType) {
158: _annotationType = annotationType;
159: }
160:
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,
216: final String stringValue) {
217: _elementValues.put(elementName, stringValue);
218: }
219:
220: /**
221: * Adds an annotation element name=list pair.
222: *
223: * @param elementName Name of this annotation element.
224: * @param stringValues String array value of this annotation element.
225: */
226: public void setElementValue(final String elementName,
227: final String[] stringValues) {
228: _elementValues.put(elementName, stringValues);
229: }
230:
231: /**
232: * Adds an annotation element name=annotation pair.
233: *
234: * @param elementName Name of this annotation element.
235: * @param annotationValue Annotation to be used as the value.
236: */
237: public void setElementValue(final String elementName,
238: final JAnnotation annotationValue) {
239: _elementValues.put(elementName, annotationValue);
240: }
241:
242: /**
243: * Adds an annotation element name=array of annotations.
244: *
245: * @param elementName Name of this annotation element.
246: * @param annotationValues Array of annotations to be used as the value.
247: */
248: public void setElementValue(final String elementName,
249: final JAnnotation[] annotationValues) {
250: _elementValues.put(elementName, annotationValues);
251: }
252:
253: /**
254: * Returns the annotation element value when it is a String.
255: *
256: * @return The annotation element value.
257: */
258: public String getValue() {
259: Object elementValue = getElementValueObject(VALUE);
260: if (elementValue instanceof String) {
261: return (String) elementValue;
262: }
263: throw new IllegalStateException(
264: "'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(
278: "'value' element is not of type JAnnotation.");
279: }
280:
281: /**
282: * For the provided element name, returns the annotation element value when
283: * it is a String.
284: *
285: * @param elementName Element to return the value of.
286: * @return The annotation element value.
287: */
288: public String getElementValue(final String elementName) {
289: Object elementValue = getElementValueObject(elementName);
290: if (elementValue instanceof String) {
291: return (String) elementValue;
292: }
293: throw new IllegalStateException("'" + elementName
294: + "' element is not of type String.");
295: }
296:
297: /**
298: * For the provided element name, returns the annotation element value when
299: * it is an array of String.
300: *
301: * @param elementName Element to return the value of.
302: * @return The annotation element value.
303: */
304: public String[] getElementValueList(final String elementName) {
305: Object elementValue = getElementValueObject(elementName);
306: if (elementValue instanceof String[]) {
307: return (String[]) elementValue;
308: }
309: throw new IllegalStateException("'" + elementName
310: + "' element is not of type String[].");
311: }
312:
313: /**
314: * Returns the given annotation element value as Object, typically used if
315: * the value type is not known. This will either be a String or JAnnotation
316: * or an array of String or an array of JAnnotation.
317: *
318: * @param elementName Element to return the value of.
319: * @return Annotation element value as Object.
320: */
321: public Object getElementValueObject(final String elementName) {
322: return _elementValues.get(elementName);
323: }
324:
325: /**
326: * For the provided element name, returns the annotation element value when
327: * it is a JAnnotation.
328: *
329: * @param elementName Element to return the value of.
330: * @return The annotation element value.
331: */
332: public JAnnotation getElementValueAnnotation(
333: final String elementName) {
334: Object elementValue = getElementValueObject(elementName);
335: if (elementValue instanceof JAnnotation) {
336: return (JAnnotation) elementValue;
337: }
338: throw new IllegalStateException("'" + elementName
339: + "' element is not of type JAnnotation.");
340: }
341:
342: /**
343: * For the provided element name, returns the annotation element value when
344: * it is an array of JAnnotation.
345: *
346: * @param elementName Element to return the value of.
347: * @return The annotation element value.
348: */
349: public JAnnotation[] getElementValueAnnotationList(
350: final String elementName) {
351: Object elementValue = getElementValueObject(elementName);
352: if (elementValue instanceof JAnnotation[]) {
353: return (JAnnotation[]) elementValue;
354: }
355: throw new IllegalStateException("'" + elementName
356: + "' element is not of type JAnnotation[].");
357: }
358:
359: /**
360: * Returns the names of the elements set by this annotation.
361: *
362: * @return Array of element names.
363: */
364: public String[] getElementNames() {
365: return (String[]) _elementValues.keySet().toArray(
366: new String[_elementValues.size()]);
367: }
368:
369: //--------------------------------------------------------------------------
370:
371: /**
372: * Prints the source code for this JAnnotation to the given JSourceWriter.
373: *
374: * @param jsw the JSourceWriter to print to. Must not be null.
375: */
376: public void print(final JSourceWriter jsw) {
377: jsw.write("@");
378: jsw.write(_annotationType.getLocalName());
379: jsw.write("(");
380: // Single element annotation?
381: String[] elementNames = getElementNames();
382: if (elementNames.length == 1 && elementNames[0].equals(VALUE)) {
383: // Just output value
384: printElementValue(jsw, getElementValueObject(VALUE));
385: } else if (elementNames.length > 0) {
386: // Max element name length?
387: int maxLength = 0;
388: for (int i = 0; i < elementNames.length; i++) {
389: int elementNameLength = elementNames[i].length();
390: if (elementNameLength > maxLength) {
391: maxLength = elementNameLength;
392: }
393: }
394: // Output element name and values
395: jsw.writeln();
396: jsw.indent();
397: for (int i = 0; i < elementNames.length; i++) {
398: int elementNameLength = elementNames[i].length();
399: // Output element name with padding
400: jsw.write(elementNames[i]);
401: for (int p = 0; p < maxLength - elementNameLength; p++) {
402: jsw.write(" ");
403: }
404: // Assignment operator
405: jsw.write(" = ");
406: // Value
407: printElementValue(jsw,
408: getElementValueObject(elementNames[i]));
409: if (i < elementNames.length - 1) {
410: jsw.write(",");
411: jsw.writeln();
412: }
413: }
414: jsw.unindent();
415: }
416: jsw.write(")");
417: }
418:
419: /**
420: * Prints annotation element value according to its type: String, String[],
421: * JAnnotation or JAnnotation[].
422: *
423: * @param jsw the JSourceWriter to print to. Must not be null.
424: * @param elementValue element value to print
425: */
426: private void printElementValue(final JSourceWriter jsw,
427: final Object elementValue) {
428: // String?
429: if (elementValue instanceof String) {
430: jsw.write((String) elementValue);
431: return;
432: } else if (elementValue instanceof JAnnotation) {
433: JAnnotation annotation = (JAnnotation) elementValue;
434: annotation.print(jsw);
435: return;
436: } else if (elementValue.getClass().isArray()) {
437: // Short hand for single item list
438: int listLength = Array.getLength(elementValue);
439: if (listLength == 1) {
440: printElementValue(jsw, Array.get(elementValue, 0));
441: return;
442: }
443: // Output list items
444: jsw.indent();
445: jsw.writeln();
446: jsw.write("{");
447: jsw.writeln();
448: jsw.indent();
449: for (int i = 0; i < listLength; i++) {
450: printElementValue(jsw, Array.get(elementValue, i));
451: if (i < listLength - 1) {
452: jsw.write(",");
453: }
454: jsw.writeln();
455: }
456: jsw.unindent();
457: jsw.write("}");
458: jsw.unindent();
459: return;
460: }
461: throw new IllegalArgumentException("'" + elementValue
462: + "' was not expected.");
463: }
464:
465: //--------------------------------------------------------------------------
466: }
|