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 }