| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.dx.dex.file; |
| |
| import com.android.dx.util.AnnotatedOutput; |
| import com.android.dx.util.Hex; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Class that represents a map item. |
| */ |
| public final class MapItem extends OffsettedItem { |
| /** file alignment of this class, in bytes */ |
| private static final int ALIGNMENT = 4; |
| |
| /** write size of this class, in bytes: three <code>uint</code>s */ |
| private static final int WRITE_SIZE = (4 * 3); |
| |
| /** non-null; item type this instance covers */ |
| private final ItemType type; |
| |
| /** non-null; section this instance covers */ |
| private final Section section; |
| |
| /** |
| * null-ok; first item covered or <code>null</code> if this is |
| * a self-reference |
| */ |
| private final Item firstItem; |
| |
| /** |
| * null-ok; last item covered or <code>null</code> if this is |
| * a self-reference |
| */ |
| private final Item lastItem; |
| |
| /** |
| * > 0; count of items covered; <code>1</code> if this |
| * is a self-reference |
| */ |
| private final int itemCount; |
| |
| /** |
| * Constructs a list item with instances of this class representing |
| * the contents of the given array of sections, adding it to the |
| * given map section. |
| * |
| * @param sections non-null; the sections |
| * @param mapSection non-null; the section that the resulting map |
| * should be added to; it should be empty on entry to this method |
| */ |
| public static void addMap(Section[] sections, |
| MixedItemSection mapSection) { |
| if (sections == null) { |
| throw new NullPointerException("sections == null"); |
| } |
| |
| if (mapSection.items().size() != 0) { |
| throw new IllegalArgumentException( |
| "mapSection.items().size() != 0"); |
| } |
| |
| ArrayList<MapItem> items = new ArrayList<MapItem>(50); |
| |
| for (Section section : sections) { |
| ItemType currentType = null; |
| Item firstItem = null; |
| Item lastItem = null; |
| int count = 0; |
| |
| for (Item item : section.items()) { |
| ItemType type = item.itemType(); |
| if (type != currentType) { |
| if (count != 0) { |
| items.add(new MapItem(currentType, section, |
| firstItem, lastItem, count)); |
| } |
| currentType = type; |
| firstItem = item; |
| count = 0; |
| } |
| lastItem = item; |
| count++; |
| } |
| |
| if (count != 0) { |
| // Add a MapItem for the final items in the section. |
| items.add(new MapItem(currentType, section, |
| firstItem, lastItem, count)); |
| } else if (section == mapSection) { |
| // Add a MapItem for the self-referential section. |
| items.add(new MapItem(mapSection)); |
| } |
| } |
| |
| mapSection.add( |
| new UniformListItem<MapItem>(ItemType.TYPE_MAP_LIST, items)); |
| } |
| |
| /** |
| * Constructs an instance. |
| * |
| * @param type non-null; item type this instance covers |
| * @param section non-null; section this instance covers |
| * @param firstItem non-null; first item covered |
| * @param lastItem non-null; last item covered |
| * @param itemCount > 0; count of items covered |
| */ |
| private MapItem(ItemType type, Section section, Item firstItem, |
| Item lastItem, int itemCount) { |
| super(ALIGNMENT, WRITE_SIZE); |
| |
| if (type == null) { |
| throw new NullPointerException("type == null"); |
| } |
| |
| if (section == null) { |
| throw new NullPointerException("section == null"); |
| } |
| |
| if (firstItem == null) { |
| throw new NullPointerException("firstItem == null"); |
| } |
| |
| if (lastItem == null) { |
| throw new NullPointerException("lastItem == null"); |
| } |
| |
| if (itemCount <= 0) { |
| throw new IllegalArgumentException("itemCount <= 0"); |
| } |
| |
| this.type = type; |
| this.section = section; |
| this.firstItem = firstItem; |
| this.lastItem = lastItem; |
| this.itemCount = itemCount; |
| } |
| |
| /** |
| * Constructs a self-referential instance. This instance is meant to |
| * represent the section containing the <code>map_list</code>. |
| * |
| * @param section non-null; section this instance covers |
| */ |
| private MapItem(Section section) { |
| super(ALIGNMENT, WRITE_SIZE); |
| |
| if (section == null) { |
| throw new NullPointerException("section == null"); |
| } |
| |
| this.type = ItemType.TYPE_MAP_LIST; |
| this.section = section; |
| this.firstItem = null; |
| this.lastItem = null; |
| this.itemCount = 1; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public ItemType itemType() { |
| return ItemType.TYPE_MAP_ITEM; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public String toString() { |
| StringBuffer sb = new StringBuffer(100); |
| |
| sb.append(getClass().getName()); |
| sb.append('{'); |
| sb.append(section.toString()); |
| sb.append(' '); |
| sb.append(type.toHuman()); |
| sb.append('}'); |
| |
| return sb.toString(); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void addContents(DexFile file) { |
| // We have nothing to add. |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public final String toHuman() { |
| return toString(); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| protected void writeTo0(DexFile file, AnnotatedOutput out) { |
| int value = type.getMapValue(); |
| int offset; |
| |
| if (firstItem == null) { |
| offset = section.getFileOffset(); |
| } else { |
| offset = section.getAbsoluteItemOffset(firstItem); |
| } |
| |
| if (out.annotates()) { |
| out.annotate(0, offsetString() + ' ' + type.getTypeName() + |
| " map"); |
| out.annotate(2, " type: " + Hex.u2(value) + " // " + |
| type.toString()); |
| out.annotate(2, " unused: 0"); |
| out.annotate(4, " size: " + Hex.u4(itemCount)); |
| out.annotate(4, " offset: " + Hex.u4(offset)); |
| } |
| |
| out.writeShort(value); |
| out.writeShort(0); // unused |
| out.writeInt(itemCount); |
| out.writeInt(offset); |
| } |
| } |