/*
 *  DynDependencyManager.java v0.10 20-DEC-1999
 *  Copyright (c) TKK/TLM/Calypso
 *  Author: Alexey Mednonogov
 */

package codec.dyntree;

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 performing updates of interrelated Any and DynAny components.
 *  The reason for introducing this class is that during e.g. preparing
 *  a request by VisitorAdapter & ServerCallVisitor they may initialize the
 *  values of Any objects derived by contents, not by reference from DynAny
 *  objects originally represented in request. These DynAny objects, in their
 *  turn, might have been derived by contents, not by reference from original
 *  Any objects represented in the request. Thus, initialization done by e.g.
 *  ServerCallVisitor does not affect the original data contained in NVList.
 *  This class registers dependencies between Any and DynAny objects and then
 *  makes the content update of the original values after all derived values
 *  are initialized by e.g. ServerCallVisitor. */
final public class DynDependencyManager {

    private DynTree relations;
	private org.omg.CORBA.ORB orb;

    public DynDependencyManager() {
        relations = new DynTree();
		orb = CorbaServer.getCodecObject().getOrbGeneric().getORB();
    }

   	/** The relation between two objects set by this method means that
     *  "dependent" has been created from "origin" by content, not by
     *  reference, that is, contents of "origin" has been copied to
     *  "dependent". After the "dependent" is properly initialized,
     *  its contents must be copied back to "origin". Only two ways of
     *  calling this method are allowed: (a) "origin" is an instance of
     *  DynAny, "dependent" is an instance of Any; (b) "origin" is an
     *  instance of Any, "dependent" is an instance of DynAny. Note that
     *  relationship between "origin" and "dependent" is one-to-one,
     *  that is, "origin" may have exactly one "dependent". */
   	public void setContentDependency(Object origin, Object dependent) {

		if ((!((origin instanceof org.omg.CORBA.DynAny) && 
			   (dependent instanceof org.omg.CORBA.Any))) &&

			(!((origin instanceof org.omg.CORBA.Any) && 
			   (dependent instanceof org.omg.CORBA.DynAny)))) {

			System.out.println("DynDependencyManager::" +
				"setContentDependency(): Internal error.");
			System.exit(0);
		}
		relations.addRelation(origin, dependent,
			DynTree.RELATION_TYPE_CONTENT);
	}

   	/** The relationship between two objects set by this method means that
     *  "contained" is encapsulated into "container", thus "container"
     *  content is incomplete unless all its "contained" values are properly
     *  updated. Establishing such relationship prevents use of "container"
     *  as "dependent" for content update of its "origin", unless all members
     *  of "container" are properly updated. Unlike setContentDependency(),
     *  this relationship sets only order of updates, not actors of updates.
     *  Note that this is a "one-to-many" relationship, that is "container"
     *  may be associated with several "contained" objects. Both "container"
     *  and "contained" shall be either DynAny or Any. */
   	public void setContainerDependency(Object container, Object contained) {

		if ((!((container instanceof org.omg.CORBA.DynAny) ||
			   (container instanceof org.omg.CORBA.Any))) ||

			(!((contained instanceof org.omg.CORBA.DynAny) ||
			   (contained instanceof org.omg.CORBA.Any)))) {

			System.out.println("DynDependencyManager::" +
				"setContainerDependency(): Internal error.");
			System.exit(0);
		}
		relations.addRelation(container, contained,
			DynTree.RELATION_TYPE_CONTAINER);
	}

   	/** Perform an update of tree hierarchy created by set() methods of the
     *  class, starting from leaves and going up along the tree. Cycles are
     *  not allowed and detected during update, resulting in fail fast. At
     *  the very first stage of the iteration, all leaves are considered to
     *  be already updated. */
   	public void updateContents() {

		while (relations.isEmpty() == false) {

			ParentChildPair[] leaves = relations.getLeaves();
			if (leaves == null) {
				System.out.println("DynDependencyManager::updateContents(): "
				    + "Internal error -- cycle has been detected in tree.");
				System.exit(0);
			}
			for (int i = 0; i < leaves.length; i++) {

				if (leaves[i].type != 
					DynTree.RELATION_TYPE_CONTENT) {

					continue;
				}
				Object parent = leaves[i].parent;
				Object child = leaves[i].child;

				if ((parent == null) || (child == null)) {
					System.out.println("DynDependencyManager::" + 
						"updateContents(): Internal error (null).");
					System.exit(0);
				}

				org.omg.CORBA.DynAny dynAny = null;
				org.omg.CORBA.Any any = null;

				if (child instanceof org.omg.CORBA.Any) {

					dynAny = (org.omg.CORBA.DynAny) parent;
					any = (org.omg.CORBA.Any) child;

					try {

						if (AnyGeneric.removeAliases(
							dynAny.type()).kind().value() !=
							org.omg.CORBA.TCKind._tk_string) {

							dynAny.from_any(any);
						}
						else {
							dynAny.insert_string(any.extract_string());
						}
					}
					catch (org.omg.CORBA.DynAnyPackage.Invalid inv) {
						System.out.println("DynDependencyManager::" + 
						    "updateContents(): Internal error (from_any).");
						System.exit(0);
					}
					catch (org.omg.CORBA.DynAnyPackage.InvalidValue iv) {
						System.out.println("DynDependencyManager::" + 
						    "updateContents(): Internal error " +
							"(insert_string).");
						System.exit(0);
					}
				}
				else {
					dynAny = (org.omg.CORBA.DynAny) child;
					any = (org.omg.CORBA.Any) parent;
					AnyGeneric.dynAnyToAny(any, dynAny);
				}
			}
			relations.removeLeaves(leaves);
		}
	}    
}
