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 17 package com.android.dx.dex.file; 18 19 import com.android.dx.util.AnnotatedOutput; 20 import com.android.dx.util.Hex; 21 22 import java.util.ArrayList; 23 24 /** 25 * Class that represents a map item. 26 */ 27 public final class MapItem extends OffsettedItem { 28 /** file alignment of this class, in bytes */ 29 private static final int ALIGNMENT = 4; 30 31 /** write size of this class, in bytes: three {@code uint}s */ 32 private static final int WRITE_SIZE = (4 * 3); 33 34 /** {@code non-null;} item type this instance covers */ 35 private final ItemType type; 36 37 /** {@code non-null;} section this instance covers */ 38 private final Section section; 39 40 /** 41 * {@code null-ok;} first item covered or {@code null} if this is 42 * a self-reference 43 */ 44 private final Item firstItem; 45 46 /** 47 * {@code null-ok;} last item covered or {@code null} if this is 48 * a self-reference 49 */ 50 private final Item lastItem; 51 52 /** 53 * {@code > 0;} count of items covered; {@code 1} if this 54 * is a self-reference 55 */ 56 private final int itemCount; 57 58 /** 59 * Constructs a list item with instances of this class representing 60 * the contents of the given array of sections, adding it to the 61 * given map section. 62 * 63 * @param sections {@code non-null;} the sections 64 * @param mapSection {@code non-null;} the section that the resulting map 65 * should be added to; it should be empty on entry to this method 66 */ 67 public static void addMap(Section[] sections, 68 MixedItemSection mapSection) { 69 if (sections == null) { 70 throw new NullPointerException("sections == null"); 71 } 72 73 if (mapSection.items().size() != 0) { 74 throw new IllegalArgumentException( 75 "mapSection.items().size() != 0"); 76 } 77 78 ArrayList<MapItem> items = new ArrayList<MapItem>(50); 79 80 for (Section section : sections) { 81 ItemType currentType = null; 82 Item firstItem = null; 83 Item lastItem = null; 84 int count = 0; 85 86 for (Item item : section.items()) { 87 ItemType type = item.itemType(); 88 if (type != currentType) { 89 if (count != 0) { 90 items.add(new MapItem(currentType, section, 91 firstItem, lastItem, count)); 92 } 93 currentType = type; 94 firstItem = item; 95 count = 0; 96 } 97 lastItem = item; 98 count++; 99 } 100 101 if (count != 0) { 102 // Add a MapItem for the final items in the section. 103 items.add(new MapItem(currentType, section, 104 firstItem, lastItem, count)); 105 } else if (section == mapSection) { 106 // Add a MapItem for the self-referential section. 107 items.add(new MapItem(mapSection)); 108 } 109 } 110 111 mapSection.add( 112 new UniformListItem<MapItem>(ItemType.TYPE_MAP_LIST, items)); 113 } 114 115 /** 116 * Constructs an instance. 117 * 118 * @param type {@code non-null;} item type this instance covers 119 * @param section {@code non-null;} section this instance covers 120 * @param firstItem {@code non-null;} first item covered 121 * @param lastItem {@code non-null;} last item covered 122 * @param itemCount {@code > 0;} count of items covered 123 */ 124 private MapItem(ItemType type, Section section, Item firstItem, 125 Item lastItem, int itemCount) { 126 super(ALIGNMENT, WRITE_SIZE); 127 128 if (type == null) { 129 throw new NullPointerException("type == null"); 130 } 131 132 if (section == null) { 133 throw new NullPointerException("section == null"); 134 } 135 136 if (firstItem == null) { 137 throw new NullPointerException("firstItem == null"); 138 } 139 140 if (lastItem == null) { 141 throw new NullPointerException("lastItem == null"); 142 } 143 144 if (itemCount <= 0) { 145 throw new IllegalArgumentException("itemCount <= 0"); 146 } 147 148 this.type = type; 149 this.section = section; 150 this.firstItem = firstItem; 151 this.lastItem = lastItem; 152 this.itemCount = itemCount; 153 } 154 155 /** 156 * Constructs a self-referential instance. This instance is meant to 157 * represent the section containing the {@code map_list}. 158 * 159 * @param section {@code non-null;} section this instance covers 160 */ 161 private MapItem(Section section) { 162 super(ALIGNMENT, WRITE_SIZE); 163 164 if (section == null) { 165 throw new NullPointerException("section == null"); 166 } 167 168 this.type = ItemType.TYPE_MAP_LIST; 169 this.section = section; 170 this.firstItem = null; 171 this.lastItem = null; 172 this.itemCount = 1; 173 } 174 175 /** {@inheritDoc} */ 176 @Override 177 public ItemType itemType() { 178 return ItemType.TYPE_MAP_ITEM; 179 } 180 181 /** {@inheritDoc} */ 182 @Override 183 public String toString() { 184 StringBuffer sb = new StringBuffer(100); 185 186 sb.append(getClass().getName()); 187 sb.append('{'); 188 sb.append(section.toString()); 189 sb.append(' '); 190 sb.append(type.toHuman()); 191 sb.append('}'); 192 193 return sb.toString(); 194 } 195 196 /** {@inheritDoc} */ 197 @Override 198 public void addContents(DexFile file) { 199 // We have nothing to add. 200 } 201 202 /** {@inheritDoc} */ 203 @Override 204 public final String toHuman() { 205 return toString(); 206 } 207 208 /** {@inheritDoc} */ 209 @Override 210 protected void writeTo0(DexFile file, AnnotatedOutput out) { 211 int value = type.getMapValue(); 212 int offset; 213 214 if (firstItem == null) { 215 offset = section.getFileOffset(); 216 } else { 217 offset = section.getAbsoluteItemOffset(firstItem); 218 } 219 220 if (out.annotates()) { 221 out.annotate(0, offsetString() + ' ' + type.getTypeName() + 222 " map"); 223 out.annotate(2, " type: " + Hex.u2(value) + " // " + 224 type.toString()); 225 out.annotate(2, " unused: 0"); 226 out.annotate(4, " size: " + Hex.u4(itemCount)); 227 out.annotate(4, " offset: " + Hex.u4(offset)); 228 } 229 230 out.writeShort(value); 231 out.writeShort(0); // unused 232 out.writeInt(itemCount); 233 out.writeInt(offset); 234 } 235 } 236