content: /*
   * [The "BSD licence"]
   * Copyright (c) 2010 Ben Gruver (JesusFreke)
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   * 3. The name of the author may not be used to endorse or promote products
   *    derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  package org.jf.dexlib;
  
  import org.jf.dexlib.Util.AlignmentUtils;
  import org.jf.dexlib.Util.AnnotatedOutput;
  import org.jf.dexlib.Util.ExceptionWithContext;
  import org.jf.dexlib.Util.Input;
  
  public abstract class Item<T extends Item> implements Comparable<T> {
      /**
       * The offset of this item in the dex file, or -1 if not known
       */
      protected int offset = -1;
  
      /**
       * The index of this item in the containing section, or -1 if not known
       */
      protected int index = -1;
  
      /**
       * The DexFile that this item is associatedr with
       */
      protected final DexFile dexFile;
  
      /**
       * The constructor that is used when reading in a <code>DexFile</code>
       * @param dexFile the <code>DexFile</code> that this item is associated with
       */
      protected Item(DexFile dexFile) {
          assert dexFile != null;
  
          this.dexFile = dexFile;
      }
  
      /**
       * Read in the item from the given input stream, and initialize the index
       * @param in the <code>Input</code> object to read from
       * @param index the index within the containing section of the item being read in
       * @param readContext a <code>ReadContext</code> object to hold information that is
       * only needed while reading in a file
       */
      protected void readFrom(Input in, int index, ReadContext readContext) {
          try {
              assert AlignmentUtils.isAligned(in.getCursor(), getItemType().ItemAlignment);
  
              this.offset = in.getCursor();
              this.index = index;
  
              this.readItem(in, readContext);
          } catch (Exception ex) {
              throw addExceptionContext(ex);
          }
      }
  
      /**
       * Place the item at the given offset and index, and return the offset of the byte following this item
       * @param offset The offset to place the item at
       * @param index The index of the item within the containing section
       * @return The offset of the byte following this item
       */
      protected int placeAt(int offset, int index) {
          try {
              assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment);
              assert !dexFile.getInplace() || (offset == this.offset && this.index == index);
  
              this.offset = offset;
              this.index = index;
              return this.placeItem(offset);
          } catch (Exception ex) {
              throw addExceptionContext(ex);
          }
      }
  
      /**
       * Write and annotate this item to the output stream
       * @param out The output stream to write and annotate to
       */
      protected void writeTo(AnnotatedOutput out) {
          try {
              assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment);
              //ensure that it is being written to the same offset where it was previously placed
              assert out.getCursor() == offset;
  
              if (out.annotates()) {
                  out.annotate(0, "[" + index + "] " + this.getItemType().TypeName);
              }
  
              out.indent();
              writeItem(out);
              out.deindent();
          } catch (Exception ex) {
              throw addExceptionContext(ex);
          }
      }
  
      /**
       * Returns a human readable form of this item
       * @return a human readable form of this item
       */
      public String toString() {
          return getConciseIdentity();
      }
  
      /**
       * The method in the concrete item subclass that actually reads in the data for the item
       *
       * The logic in this method can assume that the given Input object is valid and is
       * aligned as neccessary.
       *
       * This method is for internal use only
       * @param in the <code>Input</code> object to read from
       * @param readContext a <code>ReadContext</code> object to hold information that is
       * only needed while reading in a file
       */
      protected abstract void readItem(Input in, ReadContext readContext);
  
      /**
       * The method should finalize the layout of the item and return the offset of the byte
       * immediately following the item.
       *
       * The implementation of this method can assume that the offset argument has already been
       * aligned based on the item's alignment requirements
       *
       * This method is for internal use only
       * @param offset the (pre-aligned) offset to place the item at
       * @return the size of the item, in bytes
       */
      protected abstract int placeItem(int offset);
  
      /**
       * The method in the concrete item subclass that actually writes and annotates the data
       * for the item.
       *
       * The logic in this method can assume that the given Output object is valid and is
       * aligned as neccessary
       *
       * @param out The <code>AnnotatedOutput</code> object to write/annotate to
       */
      protected abstract void writeItem(AnnotatedOutput out);
  
      /**
       * This method is called to add item specific context information to an exception, to identify the "current item"
       * when the exception occured. It adds the value returned by <code>getConciseIdentity</code> as context for the
       * exception
       * @param ex The exception that occured
       * @return A RuntimeException with additional details about the item added
       */
      protected final RuntimeException addExceptionContext(Exception ex) {
          return ExceptionWithContext.withContext(ex, getConciseIdentity());
      }
  
      /**
       * @return An ItemType enum that represents the item type of this item
       */
      public abstract ItemType getItemType();
  
      /**
       * @return A concise (human-readable) string value that conveys the identity of this item
       */
      public abstract String getConciseIdentity();
  
  
      /**
       * @return the offset in the dex file where this item is located
       */
      public int getOffset() {
          return offset;
      }
  
      /**
       * @return the index of this item within the item's containing section
       */
      public int getIndex() {
          return index;
      }
  
      /**
       * @return the <code>DexFile</code> that contains this item
       */
      public DexFile getDexFile() {
          return dexFile;
      }
  }
  