/*
 * Copyright 2001-2004 Sun Microsystems, Inc.  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.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

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

import java.nio.ByteBuffer;

import com.sun.org.omg.SendingContext.CodeBase;

import com.sun.corba.se.pept.encoding.InputObject;

import com.sun.corba.se.spi.logging.CORBALogDomains;

import com.sun.corba.se.spi.orb.ORB;

import com.sun.corba.se.spi.transport.CorbaConnection;

import com.sun.corba.se.spi.ior.iiop.GIOPVersion;

import com.sun.corba.se.impl.encoding.BufferManagerFactory;
import com.sun.corba.se.impl.encoding.CodeSetComponentInfo;
import com.sun.corba.se.impl.encoding.CodeSetConversion;
import com.sun.corba.se.impl.encoding.OSFCodeSetRegistry;
import com.sun.corba.se.impl.encoding.CDRInputStream;

import com.sun.corba.se.impl.protocol.giopmsgheaders.Message;

import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.logging.OMGSystemException;

import com.sun.corba.se.impl.orbutil.ORBUtility;

/**
 * @author Harold Carr
 */
public class CDRInputObject extends CDRInputStream
    implements
        InputObject
{
    private CorbaConnection corbaConnection;
    private Message header;
    private boolean unmarshaledHeader;
    private ORB orb ;
    private ORBUtilSystemException wrapper ;
    private OMGSystemException omgWrapper ;

    public CDRInputObject(ORB orb,
                          CorbaConnection corbaConnection,
                          ByteBuffer byteBuffer,
                          Message header)
    {
        super(orb, byteBuffer, header.getSize(), header.isLittleEndian(),
              header.getGIOPVersion(), header.getEncodingVersion(),
              BufferManagerFactory.newBufferManagerRead(
                                          header.getGIOPVersion(),
                                          header.getEncodingVersion(),
                                          orb));

        this.corbaConnection = corbaConnection;
        this.orb = orb ;
        this.wrapper = ORBUtilSystemException.get( orb,
            CORBALogDomains.RPC_ENCODING ) ;
        this.omgWrapper = OMGSystemException.get( orb,
            CORBALogDomains.RPC_ENCODING ) ;

        if (orb.transportDebugFlag) {
            dprint(".CDRInputObject constructor:");
        }

        getBufferManager().init(header);

        this.header = header;

        unmarshaledHeader = false;

        setIndex(Message.GIOPMessageHeaderLength);

        setBufferLength(header.getSize());
    }

    // REVISIT - think about this some more.
    // This connection normally is accessed from the message mediator.
    // However, giop input needs to get code set info from the connetion
    // *before* the message mediator is available.
    public final CorbaConnection getConnection()
    {
        return corbaConnection;
    }

    // XREVISIT - Should the header be kept in the stream or the
    // message mediator?  Or should we not have a header and
    // have the information stored in the message mediator
    // directly?
    public Message getMessageHeader()
    {
        return header;
    }

    /**
     * Unmarshal the extended GIOP header
     * NOTE: May be fragmented, so should not be called by the ReaderThread.
     * See CorbaResponseWaitingRoomImpl.waitForResponse.  It is done
     * there in the client thread.
     */
    public void unmarshalHeader()
    {
        // Unmarshal the extended GIOP message from the buffer.

        if (!unmarshaledHeader) {
            try {
                if (((ORB)orb()).transportDebugFlag) {
                    dprint(".unmarshalHeader->: " + getMessageHeader());
                }
                getMessageHeader().read(this);
                unmarshaledHeader= true;
            } catch (RuntimeException e) {
                if (((ORB)orb()).transportDebugFlag) {
                    dprint(".unmarshalHeader: !!ERROR!!: "
                           + getMessageHeader()
                           + ": " + e);
                }
                throw e;
            } finally {
                if (((ORB)orb()).transportDebugFlag) {
                    dprint(".unmarshalHeader<-: " + getMessageHeader());
                }
            }
        }
    }

    public final boolean unmarshaledHeader()
    {
        return unmarshaledHeader;
    }

    /**
     * Override the default CDR factory behavior to get the
     * negotiated code sets from the connection.
     *
     * These are only called once per message, the first time needed.
     *
     * In the local case, there is no Connection, so use the
     * local code sets.
     */
    protected CodeSetConversion.BTCConverter createCharBTCConverter() {
        CodeSetComponentInfo.CodeSetContext codesets = getCodeSets();

        // If the connection doesn't have its negotiated
        // code sets by now, fall back on the defaults defined
        // in CDRInputStream.
        if (codesets == null)
            return super.createCharBTCConverter();

        OSFCodeSetRegistry.Entry charSet
            = OSFCodeSetRegistry.lookupEntry(codesets.getCharCodeSet());

        if (charSet == null)
            throw wrapper.unknownCodeset( charSet ) ;

        return CodeSetConversion.impl().getBTCConverter(charSet, isLittleEndian());
    }

    protected CodeSetConversion.BTCConverter createWCharBTCConverter() {

        CodeSetComponentInfo.CodeSetContext codesets = getCodeSets();

        // If the connection doesn't have its negotiated
        // code sets by now, we have to throw an exception.
        // See CORBA formal 00-11-03 13.9.2.6.
        if (codesets == null) {
            if (getConnection().isServer())
                throw omgWrapper.noClientWcharCodesetCtx() ;
            else
                throw omgWrapper.noServerWcharCodesetCmp() ;
        }

        OSFCodeSetRegistry.Entry wcharSet
            = OSFCodeSetRegistry.lookupEntry(codesets.getWCharCodeSet());

        if (wcharSet == null)
            throw wrapper.unknownCodeset( wcharSet ) ;

        // For GIOP 1.2 and UTF-16, use big endian if there is no byte
        // order marker.  (See issue 3405b)
        //
        // For GIOP 1.1 and UTF-16, use the byte order the stream if
        // there isn't (and there shouldn't be) a byte order marker.
        //
        // GIOP 1.0 doesn't have wchars.  If we're talking to a legacy ORB,
        // we do what our old ORBs did.
        if (wcharSet == OSFCodeSetRegistry.UTF_16) {
            if (getGIOPVersion().equals(GIOPVersion.V1_2))
                return CodeSetConversion.impl().getBTCConverter(wcharSet, false);
        }

        return CodeSetConversion.impl().getBTCConverter(wcharSet, isLittleEndian());
    }

    // If we're local and don't have a Connection, use the
    // local code sets, otherwise get them from the connection.
    // If the connection doesn't have negotiated code sets
    // yet, then we use ISO8859-1 for char/string and wchar/wstring
    // are illegal.
    private CodeSetComponentInfo.CodeSetContext getCodeSets() {
        if (getConnection() == null)
            return CodeSetComponentInfo.LOCAL_CODE_SETS;
        else
            return getConnection().getCodeSetContext();
    }

    public final CodeBase getCodeBase() {
        if (getConnection() == null)
            return null;
        else
            return getConnection().getCodeBase();
    }

    // -----------------------------------------------------------
    // Below this point are commented out methods with features
    // from the old stream.  We must find ways to address
    // these issues in the future.
    // -----------------------------------------------------------

    // XREVISIT
//     private XIIOPInputStream(XIIOPInputStream stream) {
//         super(stream);

//         this.conn = stream.conn;
//         this.msg = stream.msg;
//         this.unmarshaledHeader = stream.unmarshaledHeader;
//     }

    public CDRInputStream dup() {
        // XREVISIT
        return null;
        // return new XIIOPInputStream(this);
    }

    protected void dprint(String msg)
    {
        ORBUtility.dprint("CDRInputObject", msg);
    }
}

// End of file.
