//
// CIMOMCast.java
//
// The contents of this file are subject to the SNIA Public License Version 1.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.snia.org/resources/openSource.html
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is CIMOMCast.java
// 
// The Initial Developer of the Original Code is David Simons.
//
// Contributor(s):
//
//
// This source uses the Solaris WBEM APIs version 2.2. 
// Synchronization will be maintained with the Solaris WBEM APIS,
// which are being standardized through the Java Community Process.
//
//
// $Id: CIMOMCast.java,v 1.1 2003/07/17 18:32:55 kumpf Exp $
//
// $Log: CIMOMCast.java,v $
// Revision 1.1  2003/07/17 18:32:55  kumpf
// HP-BAPU: PEP-64 CIM Client and CIM Listener Java Interface
//
// Revision 1.2  2002/02/08 15:39:50  viktor
// Full process indication support and enhanced query.
//
// Revision 1.1.1.1  2001/10/31 18:15:16  karl
// import of SNIA 0_7
//
// Revision 1.4  2001/03/20 13:17:06  davidsi
// - Added proper handling of constant values
//
// Revision 1.3  2000/11/18 14:20:32  davidsi
// - Added support for new UnsignedIntX classes.
//
// Revision 1.2  2000/11/09 17:41:13  davidsi
// - Improve handling of null while attempting to cast.
//
// Revision 1.1  2000/11/06 18:38:06  davidsi
// - Initial version.
//
//
//
package org.snia.wbemcmd.cimom;


import java.math.BigInteger;
import java.util.Vector;
import java.util.Date;
import java.lang.reflect.Array;
import java.util.GregorianCalendar;
import java.util.Calendar;
import org.snia.wbem.cim.CIMDateTime;
import org.snia.wbem.cim.*;



/**
 * Provides support for casting objects into their native Java types from Strings
 * 
 * @author David Simons (david@bowfell.com)
 */
public class CIMOMCast {

    /**
     * Converts a value into a java representation that maps to that expressed
     * by the propertyDataType. If the value passed is not a string then no
     * manipulation will occur
     * 
     * @param propValue	The value to convert
     * @param propertyDataType The type which is to be converted to but in
     *           its java equivalent class
     * @exception CIMException if conversion not possible or cast error
     *
     */
    public static CIMValue castDataType(CIMValue propValue,CIMDataType propertyDataType) throws CIMException {
        //
        // Basic type checking is necessary - one can't assign an
        // array into a non-array type (similarly for references)
        //
        if (propValue==null || propValue.getValue()==null )
            return null;
        if (propertyDataType==null)
            return(propValue);

        // this is support to tolerate a switch from STRING to OBJECT type for
        // embeddedobject support   (as)
        if (propValue.getType().getType()==CIMDataType.OBJECT &&
            propertyDataType.getType()==CIMDataType.STRING) return propValue;
        if (propValue.getType().getType()==CIMDataType.CLASS &&
            propertyDataType.getType()==CIMDataType.STRING) return propValue;

        boolean isarray1=propValue.getType().isArrayType();
        boolean isarray2=propertyDataType.isArrayType();
        boolean isref1=propValue.getType().isReferenceType();
        boolean isref2=propertyDataType.isReferenceType();
        if ( ( isarray1 && !isarray2)
             ||( isref1 && !isref2)
             ||( !isarray1 && !isref1 && (isarray2 || isref2) ) )
            throw new CIMException(CIMException.CIM_ERR_INVALID_PARAMETER,
                                   "Property value type mismatch");

        Object inValue=propValue.getValue();
        //
        // If the value is an array then we must pass over its elements
        // and cast each one of them if they have brought over as a string
        //
        if (isarray1) {
            Vector result=new Vector();
            int dataType=CIMDataType.findSimpleType(propertyDataType.getType());

            if (inValue.getClass().isArray()) {
                int size=Array.getLength(inValue);
                for (int j=0;j<size;j++) {
                    Object element=(String)(Array.get(inValue,j).toString());
                    result.addElement(castObjectToDataType(element,dataType));
                }
            }
            else {
                Vector v=(Vector)inValue;
                for (int j=0;j<v.size();j++) {
                    result.addElement(castObjectToDataType(v.elementAt(j),dataType));
                }
            }
            return(new CIMValue(result,propertyDataType));
        }
        return new CIMValue(castObjectToDataType(inValue,
                                                 propertyDataType.getType()),propertyDataType);
    }

    // Pam Wright, Imation Added properly handling of constant values    
    private static Object castObjectToDataType(Object inValue,int inType) throws CIMException {

        if (inValue==null)
            return(null);

        // If inValue is not a String, then return
        if (!inValue.getClass().getName().equals("java.lang.String"))
            return(inValue);
        String value=(String)inValue;

        // Handle nullValue
        if (value.equals("NULL")) {
            return(null);
        }

	//
	// The response may include EMPTY KEYVALUES in which this
	// condition should not exist - Bapu 01/17/02
	//
        //if (value.length()==0) 
	//{
        //    return null;
        //}

        // Allow integerValue constants (hex, binary, octal, decimal)
        BigInteger constant;
        Object o=null;
        try {
            if (inType==CIMDataType.UINT8 ) {
                constant = getIntegerConstant(value);
                o=new UnsignedInt8(constant.shortValue());
            }
            else if (inType==CIMDataType.SINT8) {
                constant = getIntegerConstant(value);
                o=new Byte(constant.byteValue());
            }
            else if (inType==CIMDataType.UINT16) {
                constant = getIntegerConstant(value);
                o=new UnsignedInt16(constant.intValue());
            }
            else if (inType==CIMDataType.SINT16) {
                constant = getIntegerConstant(value);
                o=new Short(constant.shortValue());
            }
            else if (inType==CIMDataType.UINT32) {
                constant = getIntegerConstant(value);
                o=new UnsignedInt32(constant.longValue());
            }
            else if (inType==CIMDataType.SINT32) {
                constant = getIntegerConstant(value);
                o=new Integer(constant.intValue());
            }
            else if (inType==CIMDataType.UINT64) {
                constant = getIntegerConstant(value);
                o=new UnsignedInt64(constant);
            }
            else if (inType==CIMDataType.SINT64) {
                constant = getIntegerConstant(value);
                o=new Long(constant.longValue());
            }
            // Allow realValue constants - Java recognizes
            else if (inType==CIMDataType.REAL64) {
                o=new Double(value);
            }
            else if (inType==CIMDataType.REAL32) {
                o=new Float(value);
            }
            // Allow charValue constants
            else if (inType==CIMDataType.CHAR16) {
                o = getCharacterConstant(value);
            }
            // Allow stringValue constants
            else if (inType==CIMDataType.STRING) {
                o=value;
            }
            // Allow booleanValue constants - Java recognizes
            else if (inType==CIMDataType.BOOLEAN) {
                o=new Boolean(value);
            }
            // DateTime always entered as a string
            else if (inType==CIMDataType.DATETIME) {
                o=new CIMDateTime(value);
            }
            else
                throw new CIMException(CIMException.CIM_ERR_INVALID_PARAMETER,
                                       "(1)Property value cast error, need type "+inType+
                                       " from String "+value+" val="+inValue.toString()+
                                       " len="+value.length() );

        }
        catch (java.lang.IllegalArgumentException iea) {
            throw new CIMException(CIMException.CIM_ERR_INVALID_PARAMETER,
                                   "(2)Property value cast error, need type "+
                                   inType+" from String "+value+" val="+inValue.toString()+" len="+value.length());

        }
        return(o);
    }

    /** Handle integer constants including binary, hex, octal, and decimal
      * @return a BigInteger with the constant converted to a decimal value
      * @author Pam Wright, Imation
      */
    private static BigInteger getIntegerConstant(String value) {

        boolean negative = false;
        if (value.charAt(0) == '+') {
            value = value.substring(1);
        }
        else if (value.charAt(0) == '-') {
            value = value.substring(1);
            negative = true;
        }

        int radix = 10;
        int index;
        if (value.startsWith("0x") || value.startsWith("0X")) {
            value=value.substring(2);
            radix = 16;
        }
        else if ((index=value.indexOf('b')) >= 0 || (index=value.indexOf('B')) >= 0) {
            value = value.substring(0,index);
            radix = 2;
        }
        else if (value.startsWith("0")) {
            radix = 8;
        }

        if (negative) {
            value = "-" + value;
        }

        return new BigInteger(value,radix);
    }

    /** Handle character constants
      * @return a Character with the constant converted to a character value
      * @author Pam Wright, Imation
      */
    private static Character getCharacterConstant(String value) {
        if (value.charAt(0) != '\\') {
            return new Character(value.charAt(0));
        }
        if (value.equalsIgnoreCase("\\b")) {
            return new Character('\b');
        }
        if (value.equalsIgnoreCase("\\t")) {
            return new Character('\t');
        }
        if (value.equalsIgnoreCase("\\n")) {
            return new Character('\n');
        }
        if (value.equalsIgnoreCase("\\f")) {
            return new Character('\f');
        }
        if (value.equalsIgnoreCase("\\r")) {
            return new Character('\r');
        }
        if (value.equalsIgnoreCase("\\\"")) {
            return new Character('\"');
        }
        if (value.equalsIgnoreCase("\\'")) {
            return new Character('\'');
        }
        if (value.equalsIgnoreCase("\\\\")) {
            return new Character('\\');
        }
        return new Character(value.charAt(0));
    }
}
