Home | History | Annotate | Download | only in file
      1 /*
      2  * Copyright (C) 2007 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 import java.util.List;
     22 
     23 /**
     24  * Class that represents a contiguous list of uniform items. Each
     25  * item in the list, in particular, must have the same write size and
     26  * alignment.
     27  *
     28  * <p>This class inherits its alignment from its items, bumped up to
     29  * {@code 4} if the items have a looser alignment requirement. If
     30  * it is more than {@code 4}, then there will be a gap after the
     31  * output list size (which is four bytes) and before the first item.</p>
     32  *
     33  * @param <T> type of element contained in an instance
     34  */
     35 public final class UniformListItem<T extends OffsettedItem>
     36         extends OffsettedItem {
     37     /** the size of the list header */
     38     private static final int HEADER_SIZE = 4;
     39 
     40     /** {@code non-null;} the item type */
     41     private final ItemType itemType;
     42 
     43     /** {@code non-null;} the contents */
     44     private final List<T> items;
     45 
     46     /**
     47      * Constructs an instance. It is illegal to modify the given list once
     48      * it is used to construct an instance of this class.
     49      *
     50      * @param itemType {@code non-null;} the type of the item
     51      * @param items {@code non-null and non-empty;} list of items to represent
     52      */
     53     public UniformListItem(ItemType itemType, List<T> items) {
     54         super(getAlignment(items), writeSize(items));
     55 
     56         if (itemType == null) {
     57             throw new NullPointerException("itemType == null");
     58         }
     59 
     60         this.items = items;
     61         this.itemType = itemType;
     62     }
     63 
     64     /**
     65      * Helper for {@link #UniformListItem}, which returns the alignment
     66      * requirement implied by the given list. See the header comment for
     67      * more details.
     68      *
     69      * @param items {@code non-null;} list of items being represented
     70      * @return {@code >= 4;} the alignment requirement
     71      */
     72     private static int getAlignment(List<? extends OffsettedItem> items) {
     73         try {
     74             // Since they all must have the same alignment, any one will do.
     75             return Math.max(HEADER_SIZE, items.get(0).getAlignment());
     76         } catch (IndexOutOfBoundsException ex) {
     77             // Translate the exception.
     78             throw new IllegalArgumentException("items.size() == 0");
     79         } catch (NullPointerException ex) {
     80             // Translate the exception.
     81             throw new NullPointerException("items == null");
     82         }
     83     }
     84 
     85     /**
     86      * Calculates the write size for the given list.
     87      *
     88      * @param items {@code non-null;} the list in question
     89      * @return {@code >= 0;} the write size
     90      */
     91     private static int writeSize(List<? extends OffsettedItem> items) {
     92         /*
     93          * This class assumes all included items are the same size,
     94          * an assumption which is verified in place0().
     95          */
     96         OffsettedItem first = items.get(0);
     97         return (items.size() * first.writeSize()) + getAlignment(items);
     98     }
     99 
    100     /** {@inheritDoc} */
    101     @Override
    102     public ItemType itemType() {
    103         return itemType;
    104     }
    105 
    106     /** {@inheritDoc} */
    107     @Override
    108     public String toString() {
    109         StringBuffer sb = new StringBuffer(100);
    110 
    111         sb.append(getClass().getName());
    112         sb.append(items);
    113 
    114         return sb.toString();
    115     }
    116 
    117     /** {@inheritDoc} */
    118     @Override
    119     public void addContents(DexFile file) {
    120         for (OffsettedItem i : items) {
    121             i.addContents(file);
    122         }
    123     }
    124 
    125     /** {@inheritDoc} */
    126     @Override
    127     public final String toHuman() {
    128         StringBuffer sb = new StringBuffer(100);
    129         boolean first = true;
    130 
    131         sb.append("{");
    132 
    133         for (OffsettedItem i : items) {
    134             if (first) {
    135                 first = false;
    136             } else {
    137                 sb.append(", ");
    138             }
    139             sb.append(i.toHuman());
    140         }
    141 
    142         sb.append("}");
    143         return sb.toString();
    144     }
    145 
    146     /**
    147      * Gets the underlying list of items.
    148      *
    149      * @return {@code non-null;} the list
    150      */
    151     public final List<T> getItems() {
    152         return items;
    153     }
    154 
    155     /** {@inheritDoc} */
    156     @Override
    157     protected void place0(Section addedTo, int offset) {
    158         offset += headerSize();
    159 
    160         boolean first = true;
    161         int theSize = -1;
    162         int theAlignment = -1;
    163 
    164         for (OffsettedItem i : items) {
    165             int size = i.writeSize();
    166             if (first) {
    167                 theSize = size;
    168                 theAlignment = i.getAlignment();
    169                 first = false;
    170             } else {
    171                 if (size != theSize) {
    172                     throw new UnsupportedOperationException(
    173                             "item size mismatch");
    174                 }
    175                 if (i.getAlignment() != theAlignment) {
    176                     throw new UnsupportedOperationException(
    177                             "item alignment mismatch");
    178                 }
    179             }
    180 
    181             offset = i.place(addedTo, offset) + size;
    182         }
    183     }
    184 
    185     /** {@inheritDoc} */
    186     @Override
    187     protected void writeTo0(DexFile file, AnnotatedOutput out) {
    188         int size = items.size();
    189 
    190         if (out.annotates()) {
    191             out.annotate(0, offsetString() + " " + typeName());
    192             out.annotate(4, "  size: " + Hex.u4(size));
    193         }
    194 
    195         out.writeInt(size);
    196 
    197         for (OffsettedItem i : items) {
    198             i.writeTo(file, out);
    199         }
    200     }
    201 
    202     /**
    203      * Get the size of the header of this list.
    204      *
    205      * @return {@code >= 0;} the header size
    206      */
    207     private int headerSize() {
    208         /*
    209          * Because of how this instance was set up, this is the same
    210          * as the alignment.
    211          */
    212         return getAlignment();
    213     }
    214 }
    215