//////////////////////////////////////////////////////////////////////////// 
// 
// Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1993, 1994, 1995.
// Unpublished work.  All Rights Reserved.
// 
// The software contained on this media is the property of the
// DSTC Pty Ltd.  Use of this software is strictly in accordance
// with the license agreement in the accompanying LICENSE.DOC 
// file. If your distribution of this software does not contain 
// a LICENSE.DOC file then you have no rights to use this 
// software in any manner and should contact DSTC at the address 
// below to determine an appropriate licensing arrangement.
// 
//      DSTC Pty Ltd
//      Level 7, GP South
//      University of Queensland
//      St Lucia, 4072
//      Australia
//      Tel: +61 7 3365 4310
//      Fax: +61 7 3365 4311
//      Email: jcsi@dstc.qut.edu.au
// 
// This software is being provided "AS IS" without warranty of
// any kind.  In no event shall DSTC Pty Ltd be liable for
// damage of any kind arising out of or in connection with
// the use or performance of this software.
// 
//////////////////////////////////////////////////////////////////////////// 

package com.dstc.security.asn1;

import java.io.*;
import java.util.Vector;

/**
 * <p>A class representing ASN.1 Constructed types.
 *
 * @see com.dstc.security.asn1.Asn1
 *
 * @version 0.98, 98/07/01
 * @author Ming Yung
 */
public class Constructed extends Asn1
{
  protected Vector components;
  protected String typeName = "";

  public Constructed()
  {
    components = new Vector();
  }

  public static Asn1 decode(byte encoded[], int marks[]) throws Asn1Exception
  {
    //Test class and tag and delegate accordingly (modify for long tag numbers)
    byte cl = (byte)(encoded[marks[0]] & MASK_CLASS);
    byte tag = (byte)(encoded[marks[0]] & MASK_NUMBER);

    if (cl == TAG_CLASS_UNIVERSAL)
    {
      if (tag  == TAG_SEQUENCE)
      {
        return Sequence.decode(encoded, marks);
      }
      else if (tag  == TAG_SETOF)
      {
        return SetOf.decode(encoded, marks);
      }
      else
      {
        //Implicit tag really, or Constructed OctetString etc
        Constructed retval = new Constructed();
        retval.doDecode(encoded, marks);
        return retval;
      }
    }
    else 
    {
      return Explicit.decode(encoded, marks);
    }
  }

  public Vector getComponents()
  {
    return components;
  }

  protected void doTag(byte encoding[])
  {
    encoding[0] = (byte)(tagNum | TAG_CONSTRUCTED | classType);
  }

  public void addComponent(Asn1 comp)
  {
    components.addElement(comp);
  }

  /**
   * Returns concatenation of DER encodings of components if non-empty
   * or a zero-length array if empty
   */
  protected byte[] doContents() throws IOException
  {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    
    for (int i=0; i<components.size(); i++)
    {
      bos.write(((Asn1)components.elementAt(i)).encode());
    }
    return bos.toByteArray();
  }

  /**
   * Decodes the passed-in DER encoding
   */
  protected void doDecode(byte encoded[], int marks[]) throws Asn1Exception
  {
    //Set up pointer to first component (and if a definite encoding
    //to end of contents)
    int marksIntern[] = new int[2];
    marksIntern[0] = decodeLengthOctets(encoded, marks); 

    //Return if there are no contents
    if (marksIntern[0] < 0) return;

    while (true) 
    {
      //Decode the component
      components.addElement(Asn1.decode(encoded, marksIntern));

      //Set up pointer to next component
      marksIntern[0] = marksIntern[1];

      //Exit if end of contents is reached (definite-length case)
      if (marksIntern[0] >= marks[1]) break;

      //or if we hit 00 00 (indefinite-length case)
      if (encoded[marksIntern[0]] == 0x00 & encoded[marksIntern[0]+1] == 0x00)
      {
        marks[1] = marksIntern[0] + 2;
        break;
      } 
    }
  }

  public void info(int dep)
  {
    this.depth = dep+1;
    spit(); System.out.println(typeName + " {");
    for (int i=0; i<components.size(); i++)
    {
      ((Asn1)components.elementAt(i)).info(depth);
    }
    spit(); System.out.println("}");
  }
}
