1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.dex.file; 18 19import com.android.dx.rop.annotation.Annotation; 20import com.android.dx.rop.annotation.AnnotationVisibility; 21import com.android.dx.rop.annotation.NameValuePair; 22import com.android.dx.rop.cst.Constant; 23import com.android.dx.rop.cst.CstString; 24import com.android.dx.util.ByteArrayAnnotatedOutput; 25import com.android.dx.util.AnnotatedOutput; 26 27import java.util.Arrays; 28import java.util.Comparator; 29 30/** 31 * Single annotation, which consists of a type and a set of name-value 32 * element pairs. 33 */ 34public final class AnnotationItem extends OffsettedItem { 35 /** annotation visibility constant: visible at build time only */ 36 private static final int VISIBILITY_BUILD = 0; 37 38 /** annotation visibility constant: visible at runtime */ 39 private static final int VISIBILITY_RUNTIME = 1; 40 41 /** annotation visibility constant: visible at runtime only to system */ 42 private static final int VISIBILITY_SYSTEM = 2; 43 44 /** the required alignment for instances of this class */ 45 private static final int ALIGNMENT = 1; 46 47 /** {@code non-null;} unique instance of {@link #TypeIdSorter} */ 48 private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter(); 49 50 /** {@code non-null;} the annotation to represent */ 51 private final Annotation annotation; 52 53 /** 54 * {@code null-ok;} type reference for the annotation type; set during 55 * {@link #addContents} 56 */ 57 private TypeIdItem type; 58 59 /** 60 * {@code null-ok;} encoded form, ready for writing to a file; set during 61 * {@link #place0} 62 */ 63 private byte[] encodedForm; 64 65 /** 66 * Comparator that sorts (outer) instances by type id index. 67 */ 68 private static class TypeIdSorter implements Comparator<AnnotationItem> { 69 /** {@inheritDoc} */ 70 public int compare(AnnotationItem item1, AnnotationItem item2) { 71 int index1 = item1.type.getIndex(); 72 int index2 = item2.type.getIndex(); 73 74 if (index1 < index2) { 75 return -1; 76 } else if (index1 > index2) { 77 return 1; 78 } 79 80 return 0; 81 } 82 } 83 84 /** 85 * Sorts an array of instances, in place, by type id index, 86 * ignoring all other aspects of the elements. This is only valid 87 * to use after type id indices are known. 88 * 89 * @param array {@code non-null;} array to sort 90 */ 91 public static void sortByTypeIdIndex(AnnotationItem[] array) { 92 Arrays.sort(array, TYPE_ID_SORTER); 93 } 94 95 /** 96 * Constructs an instance. 97 * 98 * @param annotation {@code non-null;} annotation to represent 99 */ 100 public AnnotationItem(Annotation annotation) { 101 /* 102 * The write size isn't known up-front because (the variable-lengthed) 103 * leb128 type is used to represent some things. 104 */ 105 super(ALIGNMENT, -1); 106 107 if (annotation == null) { 108 throw new NullPointerException("annotation == null"); 109 } 110 111 this.annotation = annotation; 112 this.type = null; 113 this.encodedForm = null; 114 } 115 116 /** {@inheritDoc} */ 117 @Override 118 public ItemType itemType() { 119 return ItemType.TYPE_ANNOTATION_ITEM; 120 } 121 122 /** {@inheritDoc} */ 123 @Override 124 public int hashCode() { 125 return annotation.hashCode(); 126 } 127 128 /** {@inheritDoc} */ 129 @Override 130 protected int compareTo0(OffsettedItem other) { 131 AnnotationItem otherAnnotation = (AnnotationItem) other; 132 133 return annotation.compareTo(otherAnnotation.annotation); 134 } 135 136 /** {@inheritDoc} */ 137 @Override 138 public String toHuman() { 139 return annotation.toHuman(); 140 } 141 142 /** {@inheritDoc} */ 143 public void addContents(DexFile file) { 144 type = file.getTypeIds().intern(annotation.getType()); 145 ValueEncoder.addContents(file, annotation); 146 } 147 148 /** {@inheritDoc} */ 149 @Override 150 protected void place0(Section addedTo, int offset) { 151 // Encode the data and note the size. 152 153 ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); 154 ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out); 155 156 encoder.writeAnnotation(annotation, false); 157 encodedForm = out.toByteArray(); 158 159 // Add one for the visibility byte in front of the encoded annotation. 160 setWriteSize(encodedForm.length + 1); 161 } 162 163 /** 164 * Write a (listing file) annotation for this instance to the given 165 * output, that consumes no bytes of output. This is for annotating 166 * a reference to this instance at the point of the reference. 167 * 168 * @param out {@code non-null;} where to output to 169 * @param prefix {@code non-null;} prefix for each line of output 170 */ 171 public void annotateTo(AnnotatedOutput out, String prefix) { 172 out.annotate(0, prefix + "visibility: " + 173 annotation.getVisibility().toHuman()); 174 out.annotate(0, prefix + "type: " + annotation.getType().toHuman()); 175 176 for (NameValuePair pair : annotation.getNameValuePairs()) { 177 CstString name = pair.getName(); 178 Constant value = pair.getValue(); 179 180 out.annotate(0, prefix + name.toHuman() + ": " + 181 ValueEncoder.constantToHuman(value)); 182 } 183 } 184 185 /** {@inheritDoc} */ 186 @Override 187 protected void writeTo0(DexFile file, AnnotatedOutput out) { 188 boolean annotates = out.annotates(); 189 AnnotationVisibility visibility = annotation.getVisibility(); 190 191 if (annotates) { 192 out.annotate(0, offsetString() + " annotation"); 193 out.annotate(1, " visibility: VISBILITY_" + visibility); 194 } 195 196 switch (visibility) { 197 case BUILD: out.writeByte(VISIBILITY_BUILD); break; 198 case RUNTIME: out.writeByte(VISIBILITY_RUNTIME); break; 199 case SYSTEM: out.writeByte(VISIBILITY_SYSTEM); break; 200 default: { 201 // EMBEDDED shouldn't appear at the top level. 202 throw new RuntimeException("shouldn't happen"); 203 } 204 } 205 206 if (annotates) { 207 /* 208 * The output is to be annotated, so redo the work previously 209 * done by place0(), except this time annotations will actually 210 * get emitted. 211 */ 212 ValueEncoder encoder = new ValueEncoder(file, out); 213 encoder.writeAnnotation(annotation, true); 214 } else { 215 out.write(encodedForm); 216 } 217 } 218} 219