content: /*
   * Copyright (C) 2014 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 dexfuzz.rawdex;
  
  import dexfuzz.Log;
  
  public class Offset {
    /**
     * The absolute value of this offset as it was originally read.
     */
    private int originalOffset;
  
    /**
     * The Offsettable that this Offset points to.
     */
    private Offsettable offsettable;
  
    /**
     * The location of this Offset in the new file, ONLY SET IF the Offset
     * couldn't be written because what it points to hasn't been written
     * yet.
     */
    private int outputLocation;
  
    /**
     * Was the output location for this Offset set?.
     */
    private boolean outputLocationSet;
  
    /**
     * Does this Offset need to be written out using ULEB128?.
     */
    private boolean useUleb128;
  
    /**
     * Was this Offset created after reading, during mutation?.
     */
    private boolean isNewOffset;
  
    /**
     * Only one Offset should have this flag set, the MapItem that points
     * to the HeaderItem.
     */
    private boolean pointsAtHeader;
  
    /**
     * If an Offset pointed at 0 (because it is not actually a valid Offset),
     * and it's not pointing at the header, then this is set.
     */
    private boolean pointsAtNull;
  
    public Offset(boolean header) {
      pointsAtHeader = header;
    }
  
    public RawDexObject getPointedToItem() {
      return offsettable.getItem();
    }
  
    public boolean pointsToSomething() {
      return offsettable != null;
    }
  
    public boolean pointsAtNull() {
      return pointsAtNull;
    }
  
    public boolean pointsAtHeader() {
      return pointsAtHeader;
    }
  
    /**
     * Returns true if this Offset points at the provided RawDexObject.
     */
    public boolean pointsToThisItem(RawDexObject thisItem) {
      if (!pointsToSomething()) {
        return false;
      }
      return (offsettable.getItem().equals(thisItem));
    }
  
    /**
     * Returns true if this Offset points at the provided Offsettable.
     */
    public boolean pointsToThisOffsettable(Offsettable thisOffsettable) {
      if (!pointsToSomething()) {
        return false;
      }
      return (offsettable.equals(thisOffsettable));
    }
  
    /**
     * Makes this Offset point at a new Offsettable.
     */
    public void pointTo(Offsettable offsettableItem) {
      if (offsettable != null) {
        Log.debug("Updating what an Offset points to...");
      }
      offsettable = offsettableItem;
    }
  
    /**
     * Call this to make an Offset that pointed at null before now point at something.
     * An Offset may have previously pointed at null before...
     * Example: if there are no fields referred to in a DEX file, then header.field_ids_off
     * will point at null. We distinguish when Offsets point at null, and are not pointing
     * at the header (only the header MapItem should do this) with a flag. Therefore, this
     * method is needed to indicate that this Offset now points at something.
     */
    public void unsetNullAndPointTo(Offsettable offsettableItem) {
      pointsAtNull = false;
      if (offsettable != null) {
        Log.debug("Updating what an Offset points to...");
      }
      offsettable = offsettableItem;
    }
  
    public void pointToNew(Offsettable offsettableItem) {
      offsettable = offsettableItem;
      isNewOffset = true;
    }
  
    public int getNewPositionOfItem() {
      return offsettable.getNewPosition();
    }
  
    public boolean usesUleb128() {
      return useUleb128;
    }
  
    /**
     * Mark this Offset as using the ULEB128 encoding.
     */
    public void setUsesUleb128() {
      if (useUleb128) {
        throw new Error("Offset is already marked as using ULEB128!");
      }
      useUleb128 = true;
    }
  
    public boolean isNewOffset() {
      return isNewOffset;
    }
  
    public void setPointsAtNull() {
      pointsAtNull = true;
    }
  
    public void setOutputLocation(int loc) {
      outputLocation = loc;
      outputLocationSet = true;
    }
  
    /**
     * Get the location in the output DEX file where this offset has been written.
     * (This is used when patching Offsets when the Offsettable position was not
     * known at the time of writing out the Offset.)
     */
    public int getOutputLocation() {
      if (!outputLocationSet) {
        throw new Error("Output location was not set yet!");
      }
      return outputLocation;
    }
  
    public void setOriginalOffset(int offset) {
      originalOffset = offset;
    }
  
    public int getOriginalOffset() {
      return originalOffset;
    }
  
    public boolean readyForWriting() {
      return offsettable.readyForFinalOffsetToBeWritten();
    }
  }
  