content: /*
   * Copyright (C) 2007 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.taobao.android.dx.dex.file;
  
  import com.taobao.android.dx.util.AnnotatedOutput;
  import java.util.Collection;
  
  /**
   * A section of a {@code .dex} file. Each section consists of a list
   * of items of some sort or other.
   */
  public abstract class Section {
      /** {@code null-ok;} name of this part, for annotation purposes */
      private final String name;
  
      /** {@code non-null;} file that this instance is part of */
      private final DexFile file;
  
      /** {@code > 0;} alignment requirement for the final output;
       * must be a power of 2 */
      private final int alignment;
  
      /** {@code >= -1;} offset from the start of the file to this part, or
       * {@code -1} if not yet known */
      private int fileOffset;
  
      /** whether {@link #prepare} has been called successfully on this
       * instance */
      private boolean prepared;
  
      /**
       * Validates an alignment.
       *
       * @param alignment the alignment
       * @throws IllegalArgumentException thrown if {@code alignment}
       * isn't a positive power of 2
       */
      public static void validateAlignment(int alignment) {
          if ((alignment <= 0) ||
              (alignment & (alignment - 1)) != 0) {
              throw new IllegalArgumentException("invalid alignment");
          }
      }
  
      /**
       * Constructs an instance. The file offset is initially unknown.
       *
       * @param name {@code null-ok;} the name of this instance, for annotation
       * purposes
       * @param file {@code non-null;} file that this instance is part of
       * @param alignment {@code > 0;} alignment requirement for the final output;
       * must be a power of 2
       */
      public Section(String name, DexFile file, int alignment) {
          if (file == null) {
              throw new NullPointerException("file == null");
          }
  
          validateAlignment(alignment);
  
          this.name = name;
          this.file = file;
          this.alignment = alignment;
          this.fileOffset = -1;
          this.prepared = false;
      }
  
      /**
       * Gets the file that this instance is part of.
       *
       * @return {@code non-null;} the file
       */
      public final DexFile getFile() {
          return file;
      }
  
      /**
       * Gets the alignment for this instance's final output.
       *
       * @return {@code > 0;} the alignment
       */
      public final int getAlignment() {
          return alignment;
      }
  
      /**
       * Gets the offset from the start of the file to this part. This
       * throws an exception if the offset has not yet been set.
       *
       * @return {@code >= 0;} the file offset
       */
      public final int getFileOffset() {
          if (fileOffset < 0) {
              throw new RuntimeException("fileOffset not set");
          }
  
          return fileOffset;
      }
  
      /**
       * Sets the file offset. It is only valid to call this method once
       * once per instance.
       *
       * @param fileOffset {@code >= 0;} the desired offset from the start of the
       * file where this for this instance
       * @return {@code >= 0;} the offset that this instance should be placed at
       * in order to meet its alignment constraint
       */
      public final int setFileOffset(int fileOffset) {
          if (fileOffset < 0) {
              throw new IllegalArgumentException("fileOffset < 0");
          }
  
          if (this.fileOffset >= 0) {
              throw new RuntimeException("fileOffset already set");
          }
  
          int mask = alignment - 1;
          fileOffset = (fileOffset + mask) & ~mask;
  
          this.fileOffset = fileOffset;
  
          return fileOffset;
      }
  
      /**
       * Writes this instance to the given raw data object.
       *
       * @param out {@code non-null;} where to write to
       */
      public final void writeTo(AnnotatedOutput out) {
          throwIfNotPrepared();
          align(out);
  
          int cursor = out.getCursor();
  
          if (fileOffset < 0) {
              fileOffset = cursor;
          } else if (fileOffset != cursor) {
              throw new RuntimeException("alignment mismatch: for " + this +
                                         ", at " + cursor +
                                         ", but expected " + fileOffset);
          }
  
          if (out.annotates()) {
              if (name != null) {
                  out.annotate(0, "\n" + name + ":");
              } else if (cursor != 0) {
                  out.annotate(0, "\n");
              }
          }
  
          writeTo0(out);
      }
  
      /**
       * Returns the absolute file offset, given an offset from the
       * start of this instance's output. This is only valid to call
       * once this instance has been assigned a file offset (via {@link
       * #setFileOffset}).
       *
       * @param relative {@code >= 0;} the relative offset
       * @return {@code >= 0;} the corresponding absolute file offset
       */
      public final int getAbsoluteOffset(int relative) {
          if (relative < 0) {
              throw new IllegalArgumentException("relative < 0");
          }
  
          if (fileOffset < 0) {
              throw new RuntimeException("fileOffset not yet set");
          }
  
          return fileOffset + relative;
      }
  
      /**
       * Returns the absolute file offset of the given item which must
       * be contained in this section. This is only valid to call
       * once this instance has been assigned a file offset (via {@link
       * #setFileOffset}).
       *
       * <p><b>Note:</b> Subclasses must implement this as appropriate for
       * their contents.</p>
       *
       * @param item {@code non-null;} the item in question
       * @return {@code >= 0;} the item's absolute file offset
       */
      public abstract int getAbsoluteItemOffset(Item item);
  
      /**
       * Prepares this instance for writing. This performs any necessary
       * prerequisites, including particularly adding stuff to other
       * sections. This method may only be called once per instance;
       * subsequent calls will throw an exception.
       */
      public final void prepare() {
          throwIfPrepared();
          prepare0();
          System.out.println("prepare type:"+name +"in "+file);
          prepared = true;
      }
  
      /**
       * Gets the collection of all the items in this section.
       * It is not valid to attempt to change the returned list.
       *
       * @return {@code non-null;} the items
       */
      public abstract Collection<? extends Item> items();
  
      /**
       * Does the main work of {@link #prepare}.
       */
      protected abstract void prepare0();
  
      /**
       * Gets the size of this instance when output, in bytes.
       *
       * @return {@code >= 0;} the size of this instance, in bytes
       */
      public abstract int writeSize();
  
      /**
       * Throws an exception if {@link #prepare} has not been
       * called on this instance.
       */
      protected final void throwIfNotPrepared() {
          if (!prepared) {
              throw new RuntimeException("not prepared");
          }
      }
  
      /**
       * Throws an exception if {@link #prepare} has already been called
       * on this instance.
       */
      protected final void throwIfPrepared() {
          if (prepared) {
              throw new RuntimeException("already prepared "+name +"in "+file);
          }
      }
  
      /**
       * Aligns the output of the given data to the alignment of this instance.
       *
       * @param out {@code non-null;} the output to align
       */
      protected final void align(AnnotatedOutput out) {
          out.alignTo(alignment);
      }
  
      /**
       * Writes this instance to the given raw data object. This gets
       * called by {@link #writeTo} after aligning the cursor of
       * {@code out} and verifying that either the assigned file
       * offset matches the actual cursor {@code out} or that the
       * file offset was not previously assigned, in which case it gets
       * assigned to {@code out}'s cursor.
       *
       * @param out {@code non-null;} where to write to
       */
      protected abstract void writeTo0(AnnotatedOutput out);
  
      /**
       * Returns the name of this section, for annotation purposes.
       *
       * @return {@code null-ok;} name of this part, for annotation purposes
       */
      protected final String getName() {
          return name;
      }
  }
  