/*
 *  InvocationAspVisitor.java v0.10 20-DEC-1999
 *  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 skeleton behaviour for Visitors focused on
 *  handling PDUs related to operation invocations. */
public class InvocationAspVisitor extends BasicAspVisitor {

    // --------------------- CLASS OUTPUT PARAMETERS --------------------

    /** Get CORBA-style operation or attribute definition. */
   	final public org.omg.CORBA.Contained getOpAttrDef() { return opAttrDef; }

    protected org.omg.CORBA.Contained opAttrDef;

   	/** Get information about whether we actually have a definition of an
	 *  attribute or of a class in a manner independent from ORB vendor. */
   	final public int getOpAttrDefClass() { return opAttrDefClass; }

   	protected int opAttrDefClass;

    public static final int DEF_OPERATION = 0;
    public static final int DEF_ATTRIBUTE = 1;

   	/** Get information about whether we are having get() or set()
	 *  operation on an attribute. */
   	final public int getAttrOptions() { return attrOptions; }

    protected int attrOptions;

    public static final int ATTR_OPTIONS_GET = 0;
    public static final int ATTR_OPTIONS_SET = 1;

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

    /** CORBA object for which request/response is being prepared. */
   	protected org.omg.CORBA.Object object;

   	/** Name scope of the requested/responded operation. */
   	protected String[] opNameScope;
    
   	/** NVList constructed by using Interface Repository. */
   	protected org.omg.CORBA.NVList nvList;

	/** Context of the requested/responded operation. */
   	protected org.omg.CORBA.Context context;

	/** Defines whether "context" clause is present in
     *  operation declaration in Interface Repository. */
	protected boolean isContextPresent;

	/** Backbone for constructing "context" field. */
	protected Vector constructedContext;

   	protected VisitorAdapter adapter;

    // ---------------------- CLASS INTERNAL STATE ----------------------

   	/** State "callidState" indicates whether "CALL_ID" field has been
     *  processed already or not. Calypso Gateway specification requires
     *  that "CALL_ID" field shall be the first field of the TTCN PDU.
     *  In case state is set to "STATE_CALLID_PARSED", "callID" field
     *  shall be set to valid value. */

   	protected int callidState;

	protected static final int STATE_CALLID_UNPARSED = 0;
    protected static final int STATE_CALLID_PARSED   = 1;

    /** See "callidState" for explanations. */
   	protected long callID;

	/** In case "isContextPresent" is set to "true", defines the state
     *  of context construction process in the similar way as it is done
     *  for "tableState". */
    protected int contextState;

   	protected static final int STATE_CONTEXT_INACTIVE = 0;
    protected static final int STATE_CONTEXT_ACTIVE   = 1;
    protected static final int STATE_CONTEXT_COMPLETE = 2;

   	protected DynIterator dynIterator;

   	/** Discriminator of last "union" statement. */
   	protected org.omg.CORBA.Any discriminator;

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

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

		super(pcoName_, asp_);

		discriminator = null;
		constructedContext = new Vector(5,5);
		callidState = STATE_CALLID_UNPARSED;
        contextState = STATE_CONTEXT_INACTIVE;
	}

	// ----------------------- CLASS POSTPROCESSOR ----------------------

   	protected void finalizeVisitor() throws com.t3.ot.misc.OtException {

        super.finalizeVisitor();

        if (callidState != STATE_CALLID_PARSED)
			throw new VisitorInvalidFormat("No CALL_ID field in PDU.");

		// Check that all in/out/inout parameters have been processed:
   		if (dynIterator.hasNext()) {

	   		throw new VisitorInvalidFormat(
		   		"PDU contains less in/out/inout parameters than " +
                "defined in Interface Repository, or parameters " +
				"definition is incomplete.");
		}
		checkContextInFinalizeVisitor();
	}

    // ----------------------- CLASS HELPER METHODS ---------------------

	final private void checkContextInFinalizeVisitor()
		throws com.t3.ot.misc.OtException {

        int i;
		org.omg.CORBA.Any any;

		if ((contextState != STATE_CONTEXT_COMPLETE) && isContextPresent) {

			throw new VisitorInvalidFormat(
				"No CONTEXT field is defined in PDU.");
		}

		if (isContextPresent && (constructedContext.size() != 0)) {

			String[] buffer = (String[]) constructedContext.
				toArray(new String[1]);

			if ((buffer.length % 2) != 0) {

				throw new VisitorInvalidFormat(
					"CONTEXT field contains odd " +
					"amount of string objects.");
			}

			for (i = 0; i < buffer.length; i += 2) {

                any = orb.create_any();
				any.insert_string(buffer[i + 1]);
				context.set_one_value(buffer[i], any);
			}
		}
	}

	/** Returns true in case value has been processed by this procedure
     *  and does not need further handling, returns false otherwise. */
    final protected boolean checkContextInCharStringValue(
		com.t3.ot.misc.StringValue value)
		throws com.t3.ot.misc.OtException {

        if ((contextState != STATE_CONTEXT_ACTIVE) ||
			(!isContextPresent)) return false;

		String strVal = value.getCharString();
		constructedContext.add(strVal);
		return true;
	}

	/** Returns true in case value has been processed by this procedure
     *  and does not need further handling, returns false otherwise. */
    final protected boolean checkContextInSequenceOfBegin(
		com.t3.ot.misc.StructValue value)
		throws com.t3.ot.misc.OtException {

		if ((contextState == STATE_CONTEXT_COMPLETE) ||
			(!isContextPresent)) return false;

		if (value.getName().equals("CONTEXT") == false) {
			throw new VisitorInvalidFormat("PDU field name " +
				"shall not be anything else than \"CONTEXT\" " +
				"in this context.");
		}

		switch (contextState) {

			case STATE_CONTEXT_INACTIVE:

				contextState = STATE_CONTEXT_ACTIVE;
				return true;

			case STATE_CONTEXT_ACTIVE:

				throw new VisitorInvalidFormat("No other SEQUENCE OF " +
					"shall be present inside SEQUENCE OF for CONTEXT.");

			default:

				System.err.println("InvocationAspVisitor::" +
				    "checkContextSequenceOfBegin(): Internal error.");
				System.exit(0);
		}
		return true;
	}

	/** Returns true in case value has been processed by this procedure
     *  and does not need further handling, returns false otherwise. */
    final protected boolean checkContextInSequenceOfEnd(
		com.t3.ot.misc.StructValue value)
		throws com.t3.ot.misc.OtException {

		if ((contextState == STATE_CONTEXT_COMPLETE) ||
			(!isContextPresent)) return false;

		if (contextState != STATE_CONTEXT_ACTIVE)
			throw new VisitorInvalidFormat("SEQUENCE OF " +
				"is defined incorrectly for CONTEXT.");

		if (value.getName().equals("CONTEXT") == false) {
			throw new VisitorInvalidFormat("PDU field name " +
				"shall not be anything else than \"CONTEXT\" " +
				"in this context.");
		}
		contextState = STATE_CONTEXT_COMPLETE;
		return true;
	}

   	/** Check that PDU field name matches the definition from Int Rep. */
   	final protected void checkAspFieldName(
		String aspFieldName, String paramName)
		throws com.t3.ot.misc.OtException {

		if (paramName.equals("")) return;

        if (paramName.equals("RET_value") && 
			aspFieldName.equals("RET_value")) return;

        if (paramName.equals("EXC_BODY") && 
			aspFieldName.equals("EXC_BODY")) return;

		int separatorIndex = -1;
		for (int i = 0; i < aspFieldName.length(); i++) {

			if (aspFieldName.charAt(i) == '_') {

				separatorIndex = i;
				break;
			}
		}
		if ((separatorIndex == 0) || (separatorIndex == -1) ||
			(separatorIndex == (aspFieldName.length() - 1))) {

			throw new VisitorInvalidFormat(
                "PDU field \"" + aspFieldName + "\" has invalid name (" +
				"name obtained from Inteface Repository: \"" + paramName +
				"\").");
		}
		String prefix = aspFieldName.substring(0, separatorIndex);

		String postfix = aspFieldName.substring(separatorIndex + 1,
			aspFieldName.length());

		if (((!prefix.equals("PAR")) && (!prefix.equals("seq")) && 
			(!prefix.equals("ch"))) || (!postfix.equals(paramName))) {

			throw new VisitorInvalidFormat(
                "PDU field \"" + aspFieldName + "\" has invalid name (" +
				"name obtained from Inteface Repository: \"" + paramName +
				"\").");
		}
	}

   	/** Check whether current PDU field has a name "seq_discriminator"
     *  and if so, save the corresponding any value to "discriminator"
     *  class field. */
    final protected void checkDiscriminator(
		DynNode dynNode, String aspFieldName)
		throws com.t3.ot.misc.OtException {

		// Additional syntactic check: in case previously checked field
        // refered to some unrelevant data, also named "seq_discriminator"
        // by coincidence, then such fake 'discriminator' will be removed:
		discriminator = null;

		if (aspFieldName.equals("seq_discriminator") == false) return;
		discriminator = dynNode.value();
	}

   	/** Obtain "kind" attribute of "namedValue". */
   	final protected static int getDynNodeKind(DynNode dynNode) {

		org.omg.CORBA.Any any = dynNode.value();
        org.omg.CORBA.TypeCode code =
			AnyGeneric.removeAliases(any.type());
        org.omg.CORBA.TCKind kind = code.kind();
        return kind.value();
	}

    /** A frequently used shortcut for checking the validity of Visitor
     *  automaton state. */
   	final protected void checkTableAndCallid()
		throws com.t3.ot.misc.OtException {

		if (tableState != STATE_TABLE_ACTIVE)
			throw new VisitorInvalidFormat(
				"Incorrect definition of TTCN table.");
        if (callidState != STATE_CALLID_PARSED)
			throw new VisitorInvalidFormat(
                "CALL_ID shall be the first field of Call PDU.");
	}

	/** A frequently used shortcut to checking the validity of DynIterator
     *  automaton state. */
   	final protected void checkDynIterator()
		throws com.t3.ot.misc.OtException {

		if (dynIterator.hasNext() == false) {

	   		throw new VisitorInvalidFormat(
		   		"PDU contains more in/out/inout parameters than " +
                "defined in Interface Repository, or additional " +
				"declarations are made.");
		}
	}

   	final protected void checkLeaf(DynNode dynNode, String aspFieldName)
		throws com.t3.ot.misc.OtException {

		if (dynNode.flags() != DynNode.FLAGS_LEAF) {

			throw new VisitorInvalidFormat(
		   		"Structural problems in PDU have been detected " +
				"(checkLeaf(): PDU field name \"" + aspFieldName +
				"\", Int. Rep. field name \"" + dynNode.name() +
				"\", Int. Rep. field type \"" + Debug.tcKindToString(
					AnyGeneric.removeAliases(dynNode.value().type()).kind()) 
				+ "\").");
		}
	}

   	final protected void checkBegOfNode(DynNode dynNode, String aspFieldName)
		throws com.t3.ot.misc.OtException {

		if (dynNode.flags() != DynNode.FLAGS_BEG_OF_NODE) {

			throw new VisitorInvalidFormat(
		   		"Structural problems in PDU have been detected " +
				"(checkBegOfNode(): PDU field name \"" + aspFieldName +
				"\", Int. Rep. field name \"" + dynNode.name() +
				"\", Int. Rep. field type \"" + Debug.tcKindToString(
					AnyGeneric.removeAliases(dynNode.value().type()).kind())
				+ "\").");
		}
	}

    final protected void checkEndOfNode(DynNode dynNode, String aspFieldName)
		throws com.t3.ot.misc.OtException {

		if (dynNode.flags() != DynNode.FLAGS_END_OF_NODE) {

			throw new VisitorInvalidFormat(
		   		"Structural problems in PDU have been detected " +
				"(checkEndOfNode(): PDU field name \"" + aspFieldName +
				"\", Int. Rep. field name \"" + dynNode.name() +
				"\", Int. Rep. field type \"" + Debug.tcKindToString(
					AnyGeneric.removeAliases(dynNode.value().type()).kind())
				+ "\").");
		}
	}
}
