content: /*
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You 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.
   */
  
  /**
  * @author Vladimir N. Molotkov, Stepan M. Mishura
  */
  
  package android.org.apache.harmony.security.asn1;
  
  import android.org.apache.harmony.security.internal.nls.Messages;
  
  import java.io.IOException;
  import java.io.InputStream;
  
  
  /**
   * This abstract class is the super class for all ASN.1 types
   * 
   * @see http://asn1.elibel.tm.fr/en/standards/index.htm
   */
  
  public abstract class ASN1Type implements ASN1Constants {
  
      /**
       * Integer representation of primitive identifier.
       */
      public final int id;
  
      /**
       * Integer representation of constructed identifier.
       */
      public final int constrId;
  
      /**
       * Constructs a primitive, universal ASN.1 type.
       * 
       * @param tagNumber - ASN.1 tag number
       * @throws IllegalArgumentException - if tagNumber is invalid
       */
      public ASN1Type(int tagNumber) {
          this(CLASS_UNIVERSAL, tagNumber);
      }
  
      /**
       * Constructs an ASN.1 type.
       * 
       * @param tagClass - tag class. MUST be
       *     CLASS_UNIVERSAL, CLASS_APPLICATION, CLASS_CONTEXTSPECIFIC, CLASS_PRIVATE
       * @param isConstructed - is ASN.1 type is a constructed type.
       * @param tagNumber - ASN.1 tag number.
       * @throws IllegalArgumentException - if tagClass or tagNumber is invalid
       */
      public ASN1Type(int tagClass, int tagNumber) {
  
          if (tagNumber < 0) {
              throw new IllegalArgumentException(Messages.getString("security.102")); //$NON-NLS-1$
          }
  
          if (tagClass != CLASS_UNIVERSAL && tagClass != CLASS_APPLICATION
                  && tagClass != CLASS_CONTEXTSPECIFIC
                  && tagClass != CLASS_PRIVATE) {
              throw new IllegalArgumentException(Messages.getString("security.103")); //$NON-NLS-1$
          }
  
          if (tagNumber < 31) {
              // short form
              this.id = tagClass + tagNumber;
          } else {
              // long form
              throw new IllegalArgumentException(
                      Messages.getString("security.104")); //$NON-NLS-1$
          }
          this.constrId = this.id + PC_CONSTRUCTED;
      }
  
      //
      //
      // Stubs for DER
      //
      //
  
      public final Object decode(byte[] encoded) throws IOException {
          return decode(new DerInputStream(encoded));
      }
  
      public final Object decode(byte[] encoded, int offset, int encodingLen)
              throws IOException {
          return decode(new DerInputStream(encoded, offset, encodingLen));
      }
  
      public final Object decode(InputStream in) throws IOException {
          return decode(new DerInputStream(in));
      }
  
      public final void verify(byte[] encoded) throws IOException {
          DerInputStream decoder = new DerInputStream(encoded);
          decoder.setVerify();
          decode(decoder);
      }
  
      public final void verify(InputStream in) throws IOException {
          DerInputStream decoder = new DerInputStream(in);
          decoder.setVerify();
          decode(decoder);
      }
  
      public final byte[] encode(Object object) {
  
          DerOutputStream out = new DerOutputStream(this, object);
          return out.encoded;
      }
  
      //
      //
      // Decode
      //
      //
  
      /**
       * Decodes ASN.1 type.
       * 
       * @param in -
       *            BER input stream
       * @throws IOException -
       *             if an I/O error occurs or the end of the stream is reached
       */
      public abstract Object decode(BerInputStream in) throws IOException;
  
      /**
       * Tests provided identifier.
       * 
       * @param identifier -
       *            identifier to be verified
       * @return - true if identifier is associated with this ASN.1 type,
       *         otherwise false
       */
      public abstract boolean checkTag(int identifier);
  
      /**
       * Creates decoded object.
       * 
       * Derived classes should override this method to provide creation for a
       * selected class of objects during decoding.
       * 
       * The default implementation returns an object created by decoding stream.
       * 
       * @param -
       *            input stream
       * @return - created object
       */
      //FIXME make me public
      protected Object getDecodedObject(BerInputStream in) throws IOException {
          return in.content;
      }
  
      //
      //
      // Encode
      //
      //
  
      /**
       * Encodes ASN.1 type.
       *
       * @param out - BER output stream
       */
      public abstract void encodeASN(BerOutputStream out);
  
      public abstract void encodeContent(BerOutputStream out);
  
      public abstract void setEncodingContent(BerOutputStream out);
  
      public int getEncodedLength(BerOutputStream out) { //FIXME name
  
          //tag length
          int len = 1; //FIXME tag length = 1. what about long form?
          //for (; tag > 0; tag = tag >> 8, len++);
  
          // length length :-)
          len++;
          if (out.length > 127) {
  
              len++;
              for (int cur = out.length >> 8; cur > 0; len++) {
                  cur = cur >> 8;
              }
          }
          len += out.length;
  
          return len;
      }
  
      public String toString() {
          // TODO decide whether this method is necessary
          //FIXME fix performance
          return this.getClass().getName() + "(tag: 0x" //$NON-NLS-1$
                  + Integer.toHexString(0xff & this.id) + ")"; //$NON-NLS-1$
      }
  }