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