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