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 }