All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jersey.repackaged.org.objectweb.asm.AnnotationWriter Maven / Gradle / Ivy

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.9
Show newest version
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package jersey.repackaged.org.objectweb.asm;

/**
 * An {@link AnnotationVisitor} that generates a corresponding 'annotation' or 'type_annotation'
 * structure, as defined in the Java Virtual Machine Specification (JVMS). AnnotationWriter
 * instances can be chained in a doubly linked list, from which Runtime[In]Visible[Type]Annotations
 * attributes can be generated with the {@link #putAnnotations} method. Similarly, arrays of such
 * lists can be used to generate Runtime[In]VisibleParameterAnnotations attributes.
 *
 * @see JVMS
 *     4.7.16
 * @see JVMS
 *     4.7.20
 * @author Eric Bruneton
 * @author Eugene Kuleshov
 */
final class AnnotationWriter extends AnnotationVisitor {

  /** Where the constants used in this AnnotationWriter must be stored. */
  private final SymbolTable symbolTable;

  /**
   * Whether values are named or not. AnnotationWriter instances used for annotation default and
   * annotation arrays use unnamed values (i.e. they generate an 'element_value' structure for each
   * value, instead of an element_name_index followed by an element_value).
   */
  private final boolean useNamedValues;

  /**
   * The 'annotation' or 'type_annotation' JVMS structure corresponding to the annotation values
   * visited so far. All the fields of these structures, except the last one - the
   * element_value_pairs array, must be set before this ByteVector is passed to the constructor
   * (num_element_value_pairs can be set to 0, it is reset to the correct value in {@link
   * #visitEnd()}). The element_value_pairs array is filled incrementally in the various visit()
   * methods.
   *
   * 

Note: as an exception to the above rules, for AnnotationDefault attributes (which contain a * single element_value by definition), this ByteVector is initially empty when passed to the * constructor, and {@link #numElementValuePairsOffset} is set to -1. */ private final ByteVector annotation; /** * The offset in {@link #annotation} where {@link #numElementValuePairs} must be stored (or -1 for * the case of AnnotationDefault attributes). */ private final int numElementValuePairsOffset; /** The number of element value pairs visited so far. */ private int numElementValuePairs; /** * The previous AnnotationWriter. This field is used to store the list of annotations of a * Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations * (annotation values of annotation type), or for AnnotationDefault attributes. */ private final AnnotationWriter previousAnnotation; /** * The next AnnotationWriter. This field is used to store the list of annotations of a * Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations * (annotation values of annotation type), or for AnnotationDefault attributes. */ private AnnotationWriter nextAnnotation; // ----------------------------------------------------------------------------------------------- // Constructors and factories // ----------------------------------------------------------------------------------------------- /** * Constructs a new {@link AnnotationWriter}. * * @param symbolTable where the constants used in this AnnotationWriter must be stored. * @param useNamedValues whether values are named or not. AnnotationDefault and annotation arrays * use unnamed values. * @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to * the visited content must be stored. This ByteVector must already contain all the fields of * the structure except the last one (the element_value_pairs array). * @param previousAnnotation the previously visited annotation of the * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or * {@literal null} in other cases (e.g. nested or array annotations). */ AnnotationWriter( final SymbolTable symbolTable, final boolean useNamedValues, final ByteVector annotation, final AnnotationWriter previousAnnotation) { super(/* latest api = */ Opcodes.ASM9); this.symbolTable = symbolTable; this.useNamedValues = useNamedValues; this.annotation = annotation; // By hypothesis, num_element_value_pairs is stored in the last unsigned short of 'annotation'. this.numElementValuePairsOffset = annotation.length == 0 ? -1 : annotation.length - 2; this.previousAnnotation = previousAnnotation; if (previousAnnotation != null) { previousAnnotation.nextAnnotation = this; } } /** * Creates a new {@link AnnotationWriter} using named values. * * @param symbolTable where the constants used in this AnnotationWriter must be stored. * @param descriptor the class descriptor of the annotation class. * @param previousAnnotation the previously visited annotation of the * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or * {@literal null} in other cases (e.g. nested or array annotations). * @return a new {@link AnnotationWriter} for the given annotation descriptor. */ static AnnotationWriter create( final SymbolTable symbolTable, final String descriptor, final AnnotationWriter previousAnnotation) { // Create a ByteVector to hold an 'annotation' JVMS structure. // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16. ByteVector annotation = new ByteVector(); // Write type_index and reserve space for num_element_value_pairs. annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); return new AnnotationWriter( symbolTable, /* useNamedValues= */ true, annotation, previousAnnotation); } /** * Creates a new {@link AnnotationWriter} using named values. * * @param symbolTable where the constants used in this AnnotationWriter must be stored. * @param typeRef a reference to the annotated type. The sort of this type reference must be * {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link * TypeReference#CLASS_TYPE_PARAMETER_BOUND} or {@link TypeReference#CLASS_EXTENDS}. See * {@link TypeReference}. * @param typePath the path to the annotated type argument, wildcard bound, array element type, or * static inner type within 'typeRef'. May be {@literal null} if the annotation targets * 'typeRef' as a whole. * @param descriptor the class descriptor of the annotation class. * @param previousAnnotation the previously visited annotation of the * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or * {@literal null} in other cases (e.g. nested or array annotations). * @return a new {@link AnnotationWriter} for the given type annotation reference and descriptor. */ static AnnotationWriter create( final SymbolTable symbolTable, final int typeRef, final TypePath typePath, final String descriptor, final AnnotationWriter previousAnnotation) { // Create a ByteVector to hold a 'type_annotation' JVMS structure. // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. ByteVector typeAnnotation = new ByteVector(); // Write target_type, target_info, and target_path. TypeReference.putTarget(typeRef, typeAnnotation); TypePath.put(typePath, typeAnnotation); // Write type_index and reserve space for num_element_value_pairs. typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); return new AnnotationWriter( symbolTable, /* useNamedValues= */ true, typeAnnotation, previousAnnotation); } // ----------------------------------------------------------------------------------------------- // Implementation of the AnnotationVisitor abstract class // ----------------------------------------------------------------------------------------------- @Override public void visit(final String name, final Object value) { // Case of an element_value with a const_value_index, class_info_index or array_index field. // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1. ++numElementValuePairs; if (useNamedValues) { annotation.putShort(symbolTable.addConstantUtf8(name)); } if (value instanceof String) { annotation.put12('s', symbolTable.addConstantUtf8((String) value)); } else if (value instanceof Byte) { annotation.put12('B', symbolTable.addConstantInteger(((Byte) value).byteValue()).index); } else if (value instanceof Boolean) { int booleanValue = ((Boolean) value).booleanValue() ? 1 : 0; annotation.put12('Z', symbolTable.addConstantInteger(booleanValue).index); } else if (value instanceof Character) { annotation.put12('C', symbolTable.addConstantInteger(((Character) value).charValue()).index); } else if (value instanceof Short) { annotation.put12('S', symbolTable.addConstantInteger(((Short) value).shortValue()).index); } else if (value instanceof Type) { annotation.put12('c', symbolTable.addConstantUtf8(((Type) value).getDescriptor())); } else if (value instanceof byte[]) { byte[] byteArray = (byte[]) value; annotation.put12('[', byteArray.length); for (byte byteValue : byteArray) { annotation.put12('B', symbolTable.addConstantInteger(byteValue).index); } } else if (value instanceof boolean[]) { boolean[] booleanArray = (boolean[]) value; annotation.put12('[', booleanArray.length); for (boolean booleanValue : booleanArray) { annotation.put12('Z', symbolTable.addConstantInteger(booleanValue ? 1 : 0).index); } } else if (value instanceof short[]) { short[] shortArray = (short[]) value; annotation.put12('[', shortArray.length); for (short shortValue : shortArray) { annotation.put12('S', symbolTable.addConstantInteger(shortValue).index); } } else if (value instanceof char[]) { char[] charArray = (char[]) value; annotation.put12('[', charArray.length); for (char charValue : charArray) { annotation.put12('C', symbolTable.addConstantInteger(charValue).index); } } else if (value instanceof int[]) { int[] intArray = (int[]) value; annotation.put12('[', intArray.length); for (int intValue : intArray) { annotation.put12('I', symbolTable.addConstantInteger(intValue).index); } } else if (value instanceof long[]) { long[] longArray = (long[]) value; annotation.put12('[', longArray.length); for (long longValue : longArray) { annotation.put12('J', symbolTable.addConstantLong(longValue).index); } } else if (value instanceof float[]) { float[] floatArray = (float[]) value; annotation.put12('[', floatArray.length); for (float floatValue : floatArray) { annotation.put12('F', symbolTable.addConstantFloat(floatValue).index); } } else if (value instanceof double[]) { double[] doubleArray = (double[]) value; annotation.put12('[', doubleArray.length); for (double doubleValue : doubleArray) { annotation.put12('D', symbolTable.addConstantDouble(doubleValue).index); } } else { Symbol symbol = symbolTable.addConstant(value); annotation.put12(".s.IFJDCS".charAt(symbol.tag), symbol.index); } } @Override public void visitEnum(final String name, final String descriptor, final String value) { // Case of an element_value with an enum_const_value field. // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1. ++numElementValuePairs; if (useNamedValues) { annotation.putShort(symbolTable.addConstantUtf8(name)); } annotation .put12('e', symbolTable.addConstantUtf8(descriptor)) .putShort(symbolTable.addConstantUtf8(value)); } @Override public AnnotationVisitor visitAnnotation(final String name, final String descriptor) { // Case of an element_value with an annotation_value field. // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1. ++numElementValuePairs; if (useNamedValues) { annotation.putShort(symbolTable.addConstantUtf8(name)); } // Write tag and type_index, and reserve 2 bytes for num_element_value_pairs. annotation.put12('@', symbolTable.addConstantUtf8(descriptor)).putShort(0); return new AnnotationWriter(symbolTable, /* useNamedValues= */ true, annotation, null); } @Override public AnnotationVisitor visitArray(final String name) { // Case of an element_value with an array_value field. // https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1 ++numElementValuePairs; if (useNamedValues) { annotation.putShort(symbolTable.addConstantUtf8(name)); } // Write tag, and reserve 2 bytes for num_values. Here we take advantage of the fact that the // end of an element_value of array type is similar to the end of an 'annotation' structure: an // unsigned short num_values followed by num_values element_value, versus an unsigned short // num_element_value_pairs, followed by num_element_value_pairs { element_name_index, // element_value } tuples. This allows us to use an AnnotationWriter with unnamed values to // visit the array elements. Its num_element_value_pairs will correspond to the number of array // elements and will be stored in what is in fact num_values. annotation.put12('[', 0); return new AnnotationWriter(symbolTable, /* useNamedValues= */ false, annotation, null); } @Override public void visitEnd() { if (numElementValuePairsOffset != -1) { byte[] data = annotation.data; data[numElementValuePairsOffset] = (byte) (numElementValuePairs >>> 8); data[numElementValuePairsOffset + 1] = (byte) numElementValuePairs; } } // ----------------------------------------------------------------------------------------------- // Utility methods // ----------------------------------------------------------------------------------------------- /** * Returns the size of a Runtime[In]Visible[Type]Annotations attribute containing this annotation * and all its predecessors (see {@link #previousAnnotation}. Also adds the attribute name * to the constant pool of the class (if not null). * * @param attributeName one of "Runtime[In]Visible[Type]Annotations", or {@literal null}. * @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing this * annotation and all its predecessors. This includes the size of the attribute_name_index and * attribute_length fields. */ int computeAnnotationsSize(final String attributeName) { if (attributeName != null) { symbolTable.addConstantUtf8(attributeName); } // The attribute_name_index, attribute_length and num_annotations fields use 8 bytes. int attributeSize = 8; AnnotationWriter annotationWriter = this; while (annotationWriter != null) { attributeSize += annotationWriter.annotation.length; annotationWriter = annotationWriter.previousAnnotation; } return attributeSize; } /** * Returns the size of the Runtime[In]Visible[Type]Annotations attributes containing the given * annotations and all their predecessors (see {@link #previousAnnotation}. Also adds the * attribute names to the constant pool of the class (if not null). * * @param lastRuntimeVisibleAnnotation The last runtime visible annotation of a field, method or * class. The previous ones can be accessed with the {@link #previousAnnotation} field. May be * {@literal null}. * @param lastRuntimeInvisibleAnnotation The last runtime invisible annotation of this a field, * method or class. The previous ones can be accessed with the {@link #previousAnnotation} * field. May be {@literal null}. * @param lastRuntimeVisibleTypeAnnotation The last runtime visible type annotation of this a * field, method or class. The previous ones can be accessed with the {@link * #previousAnnotation} field. May be {@literal null}. * @param lastRuntimeInvisibleTypeAnnotation The last runtime invisible type annotation of a * field, method or class field. The previous ones can be accessed with the {@link * #previousAnnotation} field. May be {@literal null}. * @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing the * given annotations and all their predecessors. This includes the size of the * attribute_name_index and attribute_length fields. */ static int computeAnnotationsSize( final AnnotationWriter lastRuntimeVisibleAnnotation, final AnnotationWriter lastRuntimeInvisibleAnnotation, final AnnotationWriter lastRuntimeVisibleTypeAnnotation, final AnnotationWriter lastRuntimeInvisibleTypeAnnotation) { int size = 0; if (lastRuntimeVisibleAnnotation != null) { size += lastRuntimeVisibleAnnotation.computeAnnotationsSize( Constants.RUNTIME_VISIBLE_ANNOTATIONS); } if (lastRuntimeInvisibleAnnotation != null) { size += lastRuntimeInvisibleAnnotation.computeAnnotationsSize( Constants.RUNTIME_INVISIBLE_ANNOTATIONS); } if (lastRuntimeVisibleTypeAnnotation != null) { size += lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize( Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS); } if (lastRuntimeInvisibleTypeAnnotation != null) { size += lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize( Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); } return size; } /** * Puts a Runtime[In]Visible[Type]Annotations attribute containing this annotations and all its * predecessors (see {@link #previousAnnotation} in the given ByteVector. Annotations are * put in the same order they have been visited. * * @param attributeNameIndex the constant pool index of the attribute name (one of * "Runtime[In]Visible[Type]Annotations"). * @param output where the attribute must be put. */ void putAnnotations(final int attributeNameIndex, final ByteVector output) { int attributeLength = 2; // For num_annotations. int numAnnotations = 0; AnnotationWriter annotationWriter = this; AnnotationWriter firstAnnotation = null; while (annotationWriter != null) { // In case the user forgot to call visitEnd(). annotationWriter.visitEnd(); attributeLength += annotationWriter.annotation.length; numAnnotations++; firstAnnotation = annotationWriter; annotationWriter = annotationWriter.previousAnnotation; } output.putShort(attributeNameIndex); output.putInt(attributeLength); output.putShort(numAnnotations); annotationWriter = firstAnnotation; while (annotationWriter != null) { output.putByteArray(annotationWriter.annotation.data, 0, annotationWriter.annotation.length); annotationWriter = annotationWriter.nextAnnotation; } } /** * Puts the Runtime[In]Visible[Type]Annotations attributes containing the given annotations and * all their predecessors (see {@link #previousAnnotation} in the given ByteVector. * Annotations are put in the same order they have been visited. * * @param symbolTable where the constants used in the AnnotationWriter instances are stored. * @param lastRuntimeVisibleAnnotation The last runtime visible annotation of a field, method or * class. The previous ones can be accessed with the {@link #previousAnnotation} field. May be * {@literal null}. * @param lastRuntimeInvisibleAnnotation The last runtime invisible annotation of this a field, * method or class. The previous ones can be accessed with the {@link #previousAnnotation} * field. May be {@literal null}. * @param lastRuntimeVisibleTypeAnnotation The last runtime visible type annotation of this a * field, method or class. The previous ones can be accessed with the {@link * #previousAnnotation} field. May be {@literal null}. * @param lastRuntimeInvisibleTypeAnnotation The last runtime invisible type annotation of a * field, method or class field. The previous ones can be accessed with the {@link * #previousAnnotation} field. May be {@literal null}. * @param output where the attributes must be put. */ static void putAnnotations( final SymbolTable symbolTable, final AnnotationWriter lastRuntimeVisibleAnnotation, final AnnotationWriter lastRuntimeInvisibleAnnotation, final AnnotationWriter lastRuntimeVisibleTypeAnnotation, final AnnotationWriter lastRuntimeInvisibleTypeAnnotation, final ByteVector output) { if (lastRuntimeVisibleAnnotation != null) { lastRuntimeVisibleAnnotation.putAnnotations( symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output); } if (lastRuntimeInvisibleAnnotation != null) { lastRuntimeInvisibleAnnotation.putAnnotations( symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output); } if (lastRuntimeVisibleTypeAnnotation != null) { lastRuntimeVisibleTypeAnnotation.putAnnotations( symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output); } if (lastRuntimeInvisibleTypeAnnotation != null) { lastRuntimeInvisibleTypeAnnotation.putAnnotations( symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output); } } /** * Returns the size of a Runtime[In]VisibleParameterAnnotations attribute containing all the * annotation lists from the given AnnotationWriter sub-array. Also adds the attribute name to the * constant pool of the class. * * @param attributeName one of "Runtime[In]VisibleParameterAnnotations". * @param annotationWriters an array of AnnotationWriter lists (designated by their last * element). * @param annotableParameterCount the number of elements in annotationWriters to take into account * (elements [0..annotableParameterCount[ are taken into account). * @return the size in bytes of a Runtime[In]VisibleParameterAnnotations attribute corresponding * to the given sub-array of AnnotationWriter lists. This includes the size of the * attribute_name_index and attribute_length fields. */ static int computeParameterAnnotationsSize( final String attributeName, final AnnotationWriter[] annotationWriters, final int annotableParameterCount) { // Note: attributeName is added to the constant pool by the call to computeAnnotationsSize // below. This assumes that there is at least one non-null element in the annotationWriters // sub-array (which is ensured by the lazy instantiation of this array in MethodWriter). // The attribute_name_index, attribute_length and num_parameters fields use 7 bytes, and each // element of the parameter_annotations array uses 2 bytes for its num_annotations field. int attributeSize = 7 + 2 * annotableParameterCount; for (int i = 0; i < annotableParameterCount; ++i) { AnnotationWriter annotationWriter = annotationWriters[i]; attributeSize += annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(attributeName) - 8; } return attributeSize; } /** * Puts a Runtime[In]VisibleParameterAnnotations attribute containing all the annotation lists * from the given AnnotationWriter sub-array in the given ByteVector. * * @param attributeNameIndex constant pool index of the attribute name (one of * Runtime[In]VisibleParameterAnnotations). * @param annotationWriters an array of AnnotationWriter lists (designated by their last * element). * @param annotableParameterCount the number of elements in annotationWriters to put (elements * [0..annotableParameterCount[ are put). * @param output where the attribute must be put. */ static void putParameterAnnotations( final int attributeNameIndex, final AnnotationWriter[] annotationWriters, final int annotableParameterCount, final ByteVector output) { // The num_parameters field uses 1 byte, and each element of the parameter_annotations array // uses 2 bytes for its num_annotations field. int attributeLength = 1 + 2 * annotableParameterCount; for (int i = 0; i < annotableParameterCount; ++i) { AnnotationWriter annotationWriter = annotationWriters[i]; attributeLength += annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(null) - 8; } output.putShort(attributeNameIndex); output.putInt(attributeLength); output.putByte(annotableParameterCount); for (int i = 0; i < annotableParameterCount; ++i) { AnnotationWriter annotationWriter = annotationWriters[i]; AnnotationWriter firstAnnotation = null; int numAnnotations = 0; while (annotationWriter != null) { // In case user the forgot to call visitEnd(). annotationWriter.visitEnd(); numAnnotations++; firstAnnotation = annotationWriter; annotationWriter = annotationWriter.previousAnnotation; } output.putShort(numAnnotations); annotationWriter = firstAnnotation; while (annotationWriter != null) { output.putByteArray( annotationWriter.annotation.data, 0, annotationWriter.annotation.length); annotationWriter = annotationWriter.nextAnnotation; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy