/*
 *  DynTree.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 acting as a structural backbone for DynDependencyManager.
 *  It may contain one or several tree hierarchies. */
final public class DynTree {

   	private Map relations;

    public DynTree() {
        relations = Collections.synchronizedMap(new HashMap());
    }

   	public static final int RELATION_TYPE_CONTENT   = 0;
    public static final int RELATION_TYPE_CONTAINER = 1;

   	public void addRelation(Object parent, Object child, int relationType) {

		DynTreeItem item = (DynTreeItem) relations.get(parent);
		if (item == null) {

			item = new DynTreeItem();
			item.addChild(child, relationType);
			relations.put(parent, item);
			return;
		}
		item.addChild(child, relationType);
	}

   	/** Check whether the tree contains any elements. */
   	public boolean isEmpty() {

		if (relations.size() == 0) return true; 
		else return false;
   	}

   	/** Get the parent of a child. Actual content of parent can be
     *  accessed by calling getKey() method of the returned object.
     *  @return <code>null</code> in case no parent exists (child
     *  is a root node of a tree). */
   	private Map.Entry getParent(Object child) {

		Iterator iterator = relations.entrySet().iterator();

		while (iterator.hasNext()) {

			Map.Entry entry = (Map.Entry) iterator.next();
			DynTreeItem value = (DynTreeItem) entry.getValue();
			Iterator children = value.getChildren().iterator();

			while (children.hasNext()) {
				if (children.next() == child) return entry;
			}
		}
		return null;
	}

   	/** Add leaf to the current vector of leaves. */
   	private void addLeaf(Object leaf, Vector leaves) {

		Map.Entry entry = getParent(leaf);
   		Object parent = null;
		int relationType = -1;

		if (entry != null) {

			parent = entry.getKey();
			DynTreeItem value = (DynTreeItem) entry.getValue();
			relationType = value.getRelationType();
		}
		leaves.add(new ParentChildPair(parent, leaf, relationType));
	}

   	/** Collect all tree items that do not have children. Actual content of a
     *  leaf is set in "child" field of "ParentChildPair". Root leaves will
     *  set "parent" field in "ParentChildPair" to null, otherwise it refers
     *  to existing parent. "Type" field indicates type of relation.
     *  @return <code>null</code> in case no leaves have been found. In case
     *  of erroneous situation when tree contains cycles, this does not mean
     *  that the tree is empty. This can be checked by calling isEmpty(). */
   	public ParentChildPair[] getLeaves() {

		Vector leaves = new Vector(10, 10);
		Iterator iterator = relations.entrySet().iterator();

		while (iterator.hasNext()) {

			Map.Entry entry = (Map.Entry) iterator.next();
			Object parent = entry.getKey();
			DynTreeItem value = (DynTreeItem) entry.getValue();
			Iterator children = value.getChildren().iterator();

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

				addLeaf(parent, leaves);
				continue;
			}

			while (children.hasNext()) {

				Object child = children.next();

				if (relations.get(child) == null) {

					leaves.add(new ParentChildPair(
						parent, child, value.getRelationType()));
				}
			}
		}

		if (leaves.size() == 0) return null;
		else return (ParentChildPair[]) leaves.toArray(
			new ParentChildPair[1]);
	}

   	/** Remove all leaves collected by getLeaves(). In case references in
	 *  "leaves" have been modified, behaviour of method is undefined.
     *  However, user may freely modify the contents of the objects to which
     *  "leaves[i].parent" and "leaves[i].child" point to. */
   	public void removeLeaves(ParentChildPair[] leaves) {

		for (int i = 0; i < leaves.length; i++) {

			Object parent = leaves[i].parent;
			Object child = leaves[i].child;
			relations.remove(child);

			if (parent != null) {

				DynTreeItem item = (DynTreeItem) relations.get(parent);
				if (item == null) {
					System.out.println("DynTree::removeLeaves(): " +
						"Internal error -- consistency failure (null).");
					System.exit(0);
				}
				if (item.getChildren().remove(child) == false) {
					System.out.println("DynTree::removeLeaves(): " +
						"Internal error -- consistency failure (false).");
					System.exit(0);
				}
			}
		}
	}
}
