/* * 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}s */ private static final int WRITE_SIZE = (4 * 3); /** {@code non-null;} item type this instance covers */ private final ItemType type; /** {@code non-null;} section this instance covers */ private final Section section; /** * {@code null-ok;} first item covered or {@code null} if this is * a self-reference */ private final Item firstItem; /** * {@code null-ok;} last item covered or {@code null} if this is * a self-reference */ private final Item lastItem; /** * {@code > 0;} count of items covered; {@code 1} 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 {@code non-null;} the sections * @param mapSection {@code 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 {@code non-null;} item type this instance covers * @param section {@code non-null;} section this instance covers * @param firstItem {@code non-null;} first item covered * @param lastItem {@code non-null;} last item covered * @param itemCount {@code > 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}. * * @param section {@code 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); } }