package org.jacorb.orb.dynany;

/*
 *        JacORB  - a free Java ORB
 *
 *   Copyright (C) 1997-2014 Gerald Brose / The JacORB Team.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

import java.util.ArrayList;
import java.util.List;
import org.jacorb.orb.Any;
import org.jacorb.orb.CDRInputStream;
import org.jacorb.orb.CDROutputStream;
import org.jacorb.orb.TypeCode;
import org.omg.CORBA.INTERNAL;
import org.omg.DynamicAny.DynSequenceHelper;
import org.omg.DynamicAny.DynAnyPackage.InvalidValue;
import org.omg.DynamicAny.DynAnyPackage.TypeMismatch;
import org.slf4j.Logger;

/**
 * CORBA DynSequence
 *
 * @author (c) Gerald Brose, FU Berlin 1999
 */
public final class DynSequence
   extends DynAny
   implements org.omg.DynamicAny.DynSequence
{
   private final List<org.omg.CORBA.Any> members = new ArrayList<org.omg.CORBA.Any>();
   private int length;
   private org.omg.CORBA.TypeCode elementType;

   DynSequence( org.omg.DynamicAny.DynAnyFactory dynFactory,
                org.omg.CORBA.TypeCode type,
                org.jacorb.orb.ORB orb,
                Logger logger )
           throws TypeMismatch
   {
       super(dynFactory, orb, logger);

       org.omg.CORBA.TypeCode _type = TypeCode.originalType( type );

       if( _type.kind() != org.omg.CORBA.TCKind.tk_sequence )
       {
           throw new TypeMismatch();
       }

       try
       {
           typeCode = _type;

           elementType = TypeCode.originalType( type().content_type() );
           limit = typeCode.length();
           length = 0;
       }
       catch( org.omg.CORBA.TypeCodePackage.BadKind e )
       {
           throw unexpectedException(e);
       }

       if (elementType == null)
       {
           throw new INTERNAL ("DynSequence.set_length, elementType null");
       }
   }

   public void from_any( org.omg.CORBA.Any value )
      throws InvalidValue, TypeMismatch
   {
       from_any_internal (false, value);
   }

    void from_any_internal( boolean useCurrentRepresentation, org.omg.CORBA.Any value )
      throws InvalidValue, TypeMismatch
   {
      checkDestroyed ();
      if( ! type().equivalent( value.type() ))
      {
         throw new org.omg.DynamicAny.DynAnyPackage.TypeMismatch();
      }

      try
      {
         super.from_any_internal( useCurrentRepresentation, value );

         limit = type().length();

         org.omg.CORBA.portable.InputStream is =
            value.create_input_stream();
         length = is.read_long();

         if( length > 0 )
         {
            pos = 0;
         }

         if( limit != 0 && length > limit )
         {
            throw new InvalidValue();
         }

         members.clear();
         elementType = TypeCode.originalType( type().content_type() );

         for( int i = 0 ; i < length; i++ )
         {
            Any any = (org.jacorb.orb.Any)orb.create_any();
            any.read_value( is, elementType );
            members.add( any );
         }
      }
      catch( org.omg.CORBA.TypeCodePackage.BadKind e )
      {
          throw unexpectedException(e);
      }

      if (elementType == null)
      {
          throw new INTERNAL ("DynSequence.set_length, elementType null");
      }
   }


   public org.omg.CORBA.Any to_any()
   {
      checkDestroyed();
      org.omg.CORBA.Any out_any = orb.create_any();
      out_any.type(type());

      final CDROutputStream out = new CDROutputStream(orb);
      try
      {
          out.write_long( length );

          for( int i = 0; i < length; i++)
          {
              out.write_value( elementType,
                      ((Any)members.get(i)).create_input_stream());
          }

          final CDRInputStream in = new CDRInputStream( orb, out.getBufferCopy());
          try
          {
              out_any.read_value(in, type());
              return out_any;
          }
          finally
          {
              in.close();
          }
      }
      finally
      {
          out.close();
      }
   }

   /**
    * Overrides  equal() in DynAny
    */

   public boolean equal( org.omg.DynamicAny.DynAny dyn_any )
   {
      checkDestroyed ();
      if( !type().equal( dyn_any.type())  )
      {
         return false;
      }

      org.omg.DynamicAny.DynSequence other =  DynSequenceHelper.narrow( dyn_any );

      if( other.get_length() != get_length())
      {
         return false;
      }

      org.omg.CORBA.Any[] elements = get_elements();
      org.omg.CORBA.Any[] other_elements = other.get_elements();

      for( int i = 0; i < elements.length; i++ )
      {
         if( ! (elements[i].equal( other_elements[i] )))
         {
            return false;
         }
      }

      return true;
   }

   public int get_length()
   {
      checkDestroyed ();
      return length;
   }


   public void set_length(int len)
      throws InvalidValue
   {
      checkDestroyed ();
      if( limit > 0 && len > limit )
      {
         throw new InvalidValue();
      }

      if (elementType == null)
      {
          throw new INTERNAL ("DynSequence.set_length, elementType null");
      }

      if( len == 0 )
      {
         members.clear();
         pos = -1;
      }
      else if( len > length )
      {
         try
         {
            for( int i = length; i < len; i++ )
            {
               // create correctly initialized anys
               members.add( dynFactory.create_dyn_any_from_type_code( elementType ).to_any() );
            }
         }
         catch( org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode e )
         {
             throw unexpectedException(e);
         }

         if( pos == -1 )
         {
            pos = len - length - 1;
         }
      }
      else if( len < length )
      {
          int toremove = length - len;
          for (int x=0; x < toremove; ++x)
          {
              int index = length - 1 - x;
              members.remove(index);
          }

         if( pos > len )
         {
            pos = -1;
         }
      }
      length = len;
   }


   public org.omg.CORBA.Any[] get_elements()
   {
      checkDestroyed ();
      Any[] result = new Any[ members.size()];
      for( int i = members.size(); i-- > 0; )
      {
         result[i] = (Any)members.get(i);
      }
      return result;
   }


   public void set_elements( org.omg.CORBA.Any[] value )
      throws TypeMismatch, InvalidValue
   {
      checkDestroyed ();
      if( limit > 0 && value.length > limit )
      {
         throw new InvalidValue();
      }

      for( int i = value.length; i-- > 0 ;)
      {
         org.omg.CORBA.TypeCode tc = TypeCode.originalType( value[i].type() );

         if( tc.kind() != elementType.kind() )
         {
            throw new TypeMismatch();
         }
      }

      /** ok now */
      length = value.length;

      members.clear();
      for( int i = 0; i < length; i++)
      {
         members.add( value[i] );
      }

      if( length > 0 )
      {
         pos = 0;
      }
      else
      {
         pos = -1;
      }
   }

   public org.omg.DynamicAny.DynAny[] get_elements_as_dyn_any()
   {
      checkDestroyed ();
      org.omg.DynamicAny.DynAny[] result =
         new org.omg.DynamicAny.DynAny[ members.size()];
      try
      {
         for( int i = members.size(); i-- > 0; )
         {
            result[i] = dynFactory.create_dyn_any( (Any)members.get(i));
         }
         return result;
      }
      catch( org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode e )
      {
          throw unexpectedException(e);
      }
   }


   public void set_elements_as_dyn_any(org.omg.DynamicAny.DynAny[] value)
      throws TypeMismatch, InvalidValue
   {
      checkDestroyed ();
      org.omg.CORBA.Any [] any_seq = new org.omg.CORBA.Any[value.length];
      for( int i = value.length; i-- > 0; )
      {
         any_seq[i] = value[i].to_any();
      }

      set_elements( any_seq );
   }


   public void destroy()
   {
      super.destroy();
      members.clear();
      elementType = null;
   }


   /**
    * returns the DynAny's internal any representation,
    * overwrites
    */


   protected org.omg.CORBA.Any getRepresentation()
   {
      return (Any)members.get(pos);
   }


   public boolean next()
   {
      checkDestroyed ();
      if( pos < length-1 )
      {
         pos++;
         return true;
      }
      pos = -1;
      return false;
   }

   public boolean seek(int index)
   {
      checkDestroyed ();
      if( index < 0 )
      {
         pos = -1;
         return false;
      }
      if( index < length )
      {
         pos = index;
         return true;
      }
      pos = -1;
      return false;
   }

   /* iteration interface */

   public org.omg.DynamicAny.DynAny current_component()
   {
      checkDestroyed ();
      if( pos == -1 )
      {
         return null;
      }
      try
      {
          org.omg.DynamicAny.DynAny result = dynFactory.create_dyn_any_from_type_code (members.get(pos).type());
          ((org.jacorb.orb.dynany.DynAny)result).from_any_internal(true, members.get(pos));
         return result;
      }
      catch( org.omg.DynamicAny.DynAnyPackage.InvalidValue iv )
      {
          logger.error("unable to create DynAny", iv);
          throw unexpectedException(iv);
      }
      catch( org.omg.DynamicAny.DynAnyPackage.TypeMismatch itc )
      {
          logger.error("unable to create DynAny", itc);
          throw unexpectedException(itc);
      }
      catch( org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode e )
      {
          logger.error("unable to create DynAny", e);
          throw unexpectedException(e);
      }
   }

   public int component_count()
   {
      checkDestroyed ();
      return get_length();
   }
}
