blob: d78dc9197ab1195b2d61fea5b1665b9869b2b7d2 [file] [log] [blame]
/*
* 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);
}
}