/*
 *  ClientExceptionVisitor.java v0.10 7-MAR-2000
 *  Copyright (c) TKK/TLM/Calypso
 *  Author: Alexey Mednonogov
 */

package codec.visit;

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

import codec.*;
import codec.adapt.*;
import codec.convert.*;
import codec.debug.*;
import codec.dyntree.*;
import codec.export.*;
import codec.orb.*;
import codec.pco.*;
import codec.server.*;
import codec.client.*;
import codec.visit.*;
import codec.build.*;

/** Class implementing introspection of Client Exception PDU received from
 *  Tester in order to prepare a response to Client invocation. */
final public class ClientExceptionVisitor extends ClientReplyVisitor {

	// ----------------------- INTERNAL VARIABLES -----------------------

	private int exType;

	private static final int EX_TYPE_SYSTEM = 0;
	private static final int EX_TYPE_USER   = 1;

    // ----------------------- CLASS CONSTRUCTOR ------------------------

   	public ClientExceptionVisitor(String pcoName_, com.t3.ot.pco.ASP asp_)
		throws com.t3.ot.misc.OtException {

		super(pcoName_, asp_);
        isVisitorFinalized = false;
        isContextPresent = false;

        // Get CORBA object for which request is being prepared:
		CodecObject codec = CorbaServer.getCodecObject();

        CorbaObject corbaObject =
            codec.getCorbaObjectPool().getObject(pcoName);

        if (corbaObject == null) throw new VisitorPcoUnreg();
		if (corbaObject.getPcoType() != CorbaObject.PCO_TYPE_CLIENT)
			throw new VisitorNotClient();

        object = corbaObject.getObject();

		long callID_ = 0;
		try { callID_ = asp.element("CALL_ID").getLong(); }
        catch (com.t3.ot.misc.NotFound nfe) {

			throw new VisitorInvalidFormat(
				"CALL_ID field was not found in Exception PDU.");
		}
        catch (com.t3.ot.misc.WrongType wte) {

			throw new VisitorInvalidFormat(
				"Type of CALL_ID field is other than INTEGER.");
		}
        catch (com.t3.ot.misc.OutOfRange ore) {

			throw new VisitorInvalidFormat(
				"Value of CALL_ID field is out of range.");
		}
        callID = callID_; callidState = STATE_CALLID_PARSED;

		clientRequest = codec.getClientRequestPool().getRequest(callID_);
		if (clientRequest == null) {

			throw new VisitorInvalidFormat("Exception PDU is sent as " +
                "a response to operation for which corresponding " +
			    "internal binding was not found.");
		}

        if (!clientRequest.getPcoName().equals(pcoName_)) {

            throw new VisitorInvalidFormat("Exception PDU is sent as " +
                "a response to operation which has been initially " +
                "sent through different PCO.");
		}

        if (!asp.getName().equals("pRAISE")) {

            throw new VisitorInvalidFormat("Exception PDU name is invalid.");
		}
	}

    // ------------------------ CLASS OPERATIONS ------------------------

	public void accept(com.t3.ot.pco.ASP asp_)
		throws com.t3.ot.misc.OtException {

        int i;

		try {

			// Extract name scope of the exception:

			Vector vector = new Vector(5, 5);
			Enumeration values = asp.element("EXC_SCOPE").elements();

			while (values.hasMoreElements()) {

				vector.add(((com.t3.ot.misc.Value) values.nextElement()).
					getCharString());
			}
            String[] exScope = null;

			if (vector.size() == 0) { exScope = new String[0]; }
			else { exScope = (String[]) vector.toArray(new String[1]); }

			String exName = asp.element("EXC_NAME").getCharString();
            org.omg.CORBA.Any exBody = null;

			// Analyse name scope of the exception:

			if ((exScope.length == 2) && (exScope[0].equals("CORBA"))
				&& (exScope[1].equals("SystemException")))

                { exType = EX_TYPE_SYSTEM; }
			    else { exType = EX_TYPE_USER; }

            // Extract PDU representing exception body:

            values = asp.elements();

			// Skip CALL_ID, EXC_SCOPE, EXC_NAME:
			values.nextElement();
			values.nextElement();
			values.nextElement();

			com.t3.ot.misc.Value exBodyAsp =
				(com.t3.ot.misc.Value) values.nextElement();

            // Process received exception:
            if (exType == EX_TYPE_SYSTEM) {

				// Processing system exception:

				String exID = "IDL:omg.org/CORBA/" + exName + ":1.0";

				int minor = exBodyAsp.element("EXC_BODY").
					element("seq_minor").getInt();

				int completed = exBodyAsp.element("EXC_BODY").
					element("seq_completed").getInt();

                try { org.omg.CORBA.CompletionStatus.from_int(completed); }
				catch (org.omg.CORBA.BAD_PARAM bpe) {

					throw new VisitorInvalidFormat("Field \"seq_completed\" "
                        + "in Exception PDU is out of range.");
			    }
				exBody = SystemExceptionHelper.except(
					minor, completed, exID, exName);

                clientRequest.getRequest().except(exBody);
				isVisitorFinalized = true;
				return;
			}
			else {

				// Processing user-defined exception:

				StringBuffer exIDbuffer = new StringBuffer("IDL:");

				for (i = 0; i < exScope.length; i++)
                { exIDbuffer.append(exScope[i] + "/"); }

				exIDbuffer.append(exName + ":1.0");
				String exID = exIDbuffer.toString();
				
				org.omg.CORBA.ExceptionDef exDef = null;
				exDef = Repository.getExceptionDef(exID);

				if (exDef == null) {

					throw new VisitorInvalidFormat("Exception declaration "
                        + "was not found in Interface Repository.");
				}
                exBody = orb.create_any();
				exBody.type(exDef.type());

				try {
					if (exBody.type().member_count() == 0) {

						// If exBody is empty, special handling is needed:
						clientRequest.getRequest().except(exBody);

						org.omg.CORBA.portable.OutputStream out =
							exBody.create_output_stream();

						out.write_string(exID);

						exBody.read_value(out.create_input_stream(),
							exBody.type());

						isVisitorFinalized = true;
						return;
					}
                }
				catch (org.omg.CORBA.TypeCodePackage.BadKind bke) {

				    System.err.println("ClientExceptionVisitor::" +
					    "accept(): Internal error -- unexpected " +
				        "exception \"BadKind\" thrown ");
				    System.exit(0);
				}

				// Perform initial actions so that we could use
				// "VisitorAdapter" in exception handling as easily
				// as possible:

				org.omg.CORBA.Any any = orb.create_any();
				any.type(orb.get_primitive_tc(org.omg.CORBA.TCKind.tk_void));
				org.omg.CORBA.NamedValue result = orb.create_named_value(
					"RET_value", any, org.omg.CORBA.ARG_OUT.value);

				org.omg.CORBA.NVList nvList = orb.create_list(1);
				nvList.add_value("EXC_BODY", exBody,
				    org.omg.CORBA.ARG_OUT.value);

				// Assign initial state to state Visitor automaton:
				adapter = new VisitorAdapter();
				adapter.create(nvList, result,
					VisitorAdapter.OPTIONS_CLIENT_REPLY);
				dynIterator = adapter.iterator();

                // Accept Visitor to introspect exception body:
				exBodyAsp.accept(this);
                clientRequest.getRequest().except(exBody);
			}
		}
		catch (com.t3.ot.misc.NotFound nfe) {

			throw new VisitorInvalidFormat(
				"Expected field was not found in Exception PDU.");
		}
        catch (com.t3.ot.misc.WrongType wte) {

			throw new VisitorInvalidFormat(
				"Type of Exception PDU field is other than expected.");
		}
		catch (com.t3.ot.misc.OutOfRange ore) {

			throw new VisitorInvalidFormat(
				"Value of Exception PDU field is out of range.");
		}
	}
}
