/*
 * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.corba.se.impl.dynamicany;

import org.omg.CORBA.Any;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.TCKind;
import org.omg.CORBA.portable.OutputStream;
//import org.omg.CORBA.ORBPackage.*;
import org.omg.CORBA.TypeCodePackage.BadKind;
import org.omg.CORBA.TypeCodePackage.Bounds;
import org.omg.CORBA.portable.InputStream;
import org.omg.DynamicAny.*;
import org.omg.DynamicAny.DynAnyPackage.TypeMismatch;
import org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode;
import java.math.BigDecimal;
import com.sun.corba.se.impl.corba.AnyImpl;

import com.sun.corba.se.spi.orb.ORB ;
import com.sun.corba.se.spi.logging.CORBALogDomains ;
import com.sun.corba.se.impl.logging.ORBUtilSystemException ;

public class DynAnyUtil
{
    static boolean isConsistentType(TypeCode typeCode) {
        int kind = typeCode.kind().value();
        return (kind != TCKind._tk_Principal &&
                kind != TCKind._tk_native &&
                kind != TCKind._tk_abstract_interface);
    }

    static boolean isConstructedDynAny(DynAny dynAny) {
        // DynFixed is constructed but not a subclass of DynAnyConstructedImpl
        //return (dynAny instanceof DynAnyConstructedImpl);
        int kind = dynAny.type().kind().value();
        return (kind == TCKind._tk_sequence ||
                kind == TCKind._tk_struct ||
                kind == TCKind._tk_array ||
                kind == TCKind._tk_union ||
                kind == TCKind._tk_enum ||
                kind == TCKind._tk_fixed ||
                kind == TCKind._tk_value ||
                kind == TCKind._tk_value_box);
    }

    static DynAny createMostDerivedDynAny(Any any, ORB orb, boolean copyValue)
        throws org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode
    {
        if (any == null || ! DynAnyUtil.isConsistentType(any.type()))
            throw new org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode();

        switch (any.type().kind().value()) {
            case TCKind._tk_sequence:
                return new DynSequenceImpl(orb, any, copyValue);
            case TCKind._tk_struct:
                return new DynStructImpl(orb, any, copyValue);
            case TCKind._tk_array:
                return new DynArrayImpl(orb, any, copyValue);
            case TCKind._tk_union:
                return new DynUnionImpl(orb, any, copyValue);
            case TCKind._tk_enum:
                return new DynEnumImpl(orb, any, copyValue);
            case TCKind._tk_fixed:
                return new DynFixedImpl(orb, any, copyValue);
            case TCKind._tk_value:
                return new DynValueImpl(orb, any, copyValue);
            case TCKind._tk_value_box:
                return new DynValueBoxImpl(orb, any, copyValue);
            default:
                return new DynAnyBasicImpl(orb, any, copyValue);
        }
    }

    static DynAny createMostDerivedDynAny(TypeCode typeCode, ORB orb)
        throws org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode
    {
        if (typeCode == null || ! DynAnyUtil.isConsistentType(typeCode))
            throw new org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode();

        switch (typeCode.kind().value()) {
            case TCKind._tk_sequence:
                return new DynSequenceImpl(orb, typeCode);
            case TCKind._tk_struct:
                return new DynStructImpl(orb, typeCode);
            case TCKind._tk_array:
                return new DynArrayImpl(orb, typeCode);
            case TCKind._tk_union:
                return new DynUnionImpl(orb, typeCode);
            case TCKind._tk_enum:
                return new DynEnumImpl(orb, typeCode);
            case TCKind._tk_fixed:
                return new DynFixedImpl(orb, typeCode);
            case TCKind._tk_value:
                return new DynValueImpl(orb, typeCode);
            case TCKind._tk_value_box:
                return new DynValueBoxImpl(orb, typeCode);
            default:
                return new DynAnyBasicImpl(orb, typeCode);
        }
    }

    // Extracts a member value according to the given TypeCode from the given complex Any
    // (at the Anys current internal stream position, consuming the anys stream on the way)
    // and returns it wrapped into a new Any
/*
    static Any extractAnyFromAny(TypeCode memberType, Any any, ORB orb) {
        // Moved this functionality into AnyImpl because it is needed for Any.equal()
        return ((AnyImpl)any).extractAny(memberType, orb);
    }
*/

    // Extracts a member value according to the given TypeCode from the given complex Any
    // (at the Anys current internal stream position, consuming the anys stream on the way)
    // and returns it wrapped into a new Any
    static Any extractAnyFromStream(TypeCode memberType, InputStream input, ORB orb) {
        return AnyImpl.extractAnyFromStream(memberType, input, orb);
    }

    // Creates a default Any of the given type.
    static Any createDefaultAnyOfType(TypeCode typeCode, ORB orb) {
        ORBUtilSystemException wrapper = ORBUtilSystemException.get( orb,
            CORBALogDomains.RPC_PRESENTATION ) ;

        Any returnValue = orb.create_any();
        // The spec for DynAny differs from Any on initialization via type code:
        // - false for boolean
        // - zero for numeric types
        // - zero for types octet, char, and wchar
        // - the empty string for string and wstring
        // - nil for object references
        // - a type code with a TCKind value of tk_null for type codes
        // - for Any values, an Any containing a type code with a TCKind value of tk_null
        //   type and no value
        switch (typeCode.kind().value()) {
            case TCKind._tk_boolean:
                // false for boolean
                returnValue.insert_boolean(false);
                break;
            case TCKind._tk_short:
                // zero for numeric types
                returnValue.insert_short((short)0);
                break;
            case TCKind._tk_ushort:
                // zero for numeric types
                returnValue.insert_ushort((short)0);
                break;
            case TCKind._tk_long:
                // zero for numeric types
                returnValue.insert_long(0);
                break;
            case TCKind._tk_ulong:
                // zero for numeric types
                returnValue.insert_ulong(0);
                break;
            case TCKind._tk_longlong:
                // zero for numeric types
                returnValue.insert_longlong((long)0);
                break;
            case TCKind._tk_ulonglong:
                // zero for numeric types
                returnValue.insert_ulonglong((long)0);
                break;
            case TCKind._tk_float:
                // zero for numeric types
                returnValue.insert_float((float)0.0);
                break;
            case TCKind._tk_double:
                // zero for numeric types
                returnValue.insert_double((double)0.0);
                break;
            case TCKind._tk_octet:
                // zero for types octet, char, and wchar
                returnValue.insert_octet((byte)0);
                break;
            case TCKind._tk_char:
                // zero for types octet, char, and wchar
                returnValue.insert_char((char)0);
                break;
            case TCKind._tk_wchar:
                // zero for types octet, char, and wchar
                returnValue.insert_wchar((char)0);
                break;
            case TCKind._tk_string:
                // the empty string for string and wstring
                // Make sure that type code for bounded strings gets respected
                returnValue.type(typeCode);
                // Doesn't erase the type of bounded string
                returnValue.insert_string("");
                break;
            case TCKind._tk_wstring:
                // the empty string for string and wstring
                // Make sure that type code for bounded strings gets respected
                returnValue.type(typeCode);
                // Doesn't erase the type of bounded string
                returnValue.insert_wstring("");
                break;
            case TCKind._tk_objref:
                // nil for object references
                returnValue.insert_Object(null);
                break;
            case TCKind._tk_TypeCode:
                // a type code with a TCKind value of tk_null for type codes
                // We can reuse the type code that's already in the any.
                returnValue.insert_TypeCode(returnValue.type());
                break;
            case TCKind._tk_any:
                // for Any values, an Any containing a type code with a TCKind value
                // of tk_null type and no value.
                // This is exactly what the default AnyImpl constructor provides.
                // _REVISIT_ Note that this inner Any is considered uninitialized.
                returnValue.insert_any(orb.create_any());
                break;
            case TCKind._tk_struct:
            case TCKind._tk_union:
            case TCKind._tk_enum:
            case TCKind._tk_sequence:
            case TCKind._tk_array:
            case TCKind._tk_except:
            case TCKind._tk_value:
            case TCKind._tk_value_box:
                // There are no default value for complex types since there is no
                // concept of a hierarchy of Anys. Only DynAnys can be arrange in
                // a hierarchy to mirror the TypeCode hierarchy.
                // See DynAnyConstructedImpl.initializeComponentsFromTypeCode()
                // on how this DynAny hierarchy is created from TypeCodes.
                returnValue.type(typeCode);
                break;
            case TCKind._tk_fixed:
                returnValue.insert_fixed(new BigDecimal("0.0"), typeCode);
                break;
            case TCKind._tk_native:
            case TCKind._tk_alias:
            case TCKind._tk_void:
            case TCKind._tk_Principal:
            case TCKind._tk_abstract_interface:
                returnValue.type(typeCode);
                break;
            case TCKind._tk_null:
                // Any is already initialized to null
                break;
            case TCKind._tk_longdouble:
                // Unspecified for Java
                throw wrapper.tkLongDoubleNotSupported() ;
            default:
                throw wrapper.typecodeNotSupported() ;
        }
        return returnValue;
    }
/*
    static Any setTypeOfAny(TypeCode typeCode, Any value) {
        if (value != null) {
            value.read_value(value.create_input_stream(), typeCode);
        }
        return value;
    }
*/
    static Any copy(Any inAny, ORB orb) {
        return new AnyImpl(orb, inAny);
    }

/*
    static Any copy(Any inAny, ORB orb) {
        Any outAny = null;
        if (inAny != null && orb != null) {
            outAny = orb.create_any();
            outAny.read_value(inAny.create_input_stream(), inAny.type());
            // isInitialized is set to true
        }
        return outAny;
    }
*/

    static DynAny convertToNative(DynAny dynAny, ORB orb) {
        if (dynAny instanceof DynAnyImpl) {
            return dynAny;
        } else {
            // if copy flag wasn't true we would be using our DynAny with
            // a foreign Any in it.
            try {
                return createMostDerivedDynAny(dynAny.to_any(), orb, true);
            } catch (InconsistentTypeCode ictc) {
                return null;
            }
        }
    }

    static boolean isInitialized(Any any) {
        // Returning simply the value of Any.isInitialized() is not enough.
        // The DynAny spec says that Anys containing null strings do not contain
        // a "legal value" (see ptc 99-10-07, 9.2.3.3)
        boolean isInitialized = ((AnyImpl)any).isInitialized();
        switch (any.type().kind().value()) {
            case TCKind._tk_string:
                return (isInitialized && (any.extract_string() != null));
            case TCKind._tk_wstring:
                return (isInitialized && (any.extract_wstring() != null));
        }
        return isInitialized;
    }

    // This is a convenient method to reset the current component to where it was
    // before we changed it. See DynAnyConstructedImpl.equal for use.
    static boolean set_current_component(DynAny dynAny, DynAny currentComponent) {
        if (currentComponent != null) {
            try {
                dynAny.rewind();
                do {
                    if (dynAny.current_component() == currentComponent)
                        return true;
                } while (dynAny.next());
            } catch (TypeMismatch tm) { /* impossible */ }
        }
        return false;
    }
}