/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the "License").  You may not use this file except 
 * in compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * glassfish/bootstrap/legal/CDDLv1.0.txt or 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html. 
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * HEADER in each file and include the License file at 
 * glassfish/bootstrap/legal/CDDLv1.0.txt.  If applicable, 
 * add the following below this CDDL HEADER, with the 
 * fields enclosed by brackets "[]" replaced with your 
 * own identifying information: Portions Copyright [yyyy] 
 * [name of copyright owner]
 */
// Copyright (c) 1998, 2007, Oracle. All rights reserved.  
package oracle.toplink.essentials.internal.sessions;

import java.util.*;
import oracle.toplink.essentials.descriptors.InheritancePolicy;
import oracle.toplink.essentials.mappings.*;
import oracle.toplink.essentials.internal.helper.*;
import oracle.toplink.essentials.internal.localization.*;
import oracle.toplink.essentials.descriptors.ClassDescriptor;

/**
 * This wraps a descriptor with information required to compute an order for
 * dependencies. The algorithm is a simple topological sort.
 */
public class CommitOrderDependencyNode {
    protected CommitOrderCalculator owner;
    protected ClassDescriptor descriptor;
    protected AbstractSession session;

    // These are the descriptors to which we have 1:1 relationships
    protected Vector relatedNodes;
    protected CommitOrderDependencyNode predecessor;

    // Indicates the state of the traversal
    protected int traversalState;
    static public int NotVisited = 1;
    static public int InProgress = 2;
    static public int Visited = 3;

    // When we first saw this node in the traversal
    protected int discoveryTime;

    // When we finished visiting this node
    protected int finishingTime;

    public CommitOrderDependencyNode(CommitOrderCalculator calculator, ClassDescriptor descriptor, AbstractSession session) {
        this.owner = calculator;
        this.descriptor = descriptor;
        this.relatedNodes = new Vector();
        this.session = session;
    }

    public ClassDescriptor getDescriptor() {
        return descriptor;
    }

    public int getFinishingTime() {
        return finishingTime;
    }

    public CommitOrderCalculator getOwner() {
        return owner;
    }

    public CommitOrderDependencyNode getPredecessor() {
        return predecessor;
    }

    public Vector getRelatedNodes() {
        return relatedNodes;
    }

    public boolean hasBeenVisited() {
        return (traversalState == Visited);
    }

    public boolean hasNotBeenVisited() {
        return (traversalState == NotVisited);
    }

    public void markInProgress() {
        traversalState = InProgress;
    }

    public void markNotVisited() {
        traversalState = NotVisited;
    }

    public void markVisited() {
        traversalState = Visited;
    }

    /**
     * Add all owned classes for each descriptor through checking the mappings.
     * If I have a foreign mapping with a constraint dependency, then add it
     * If I'm related to a class, I'm related to all its subclasses and superclasses.
     * If my superclass is related to a class, I'm related to it.
     */
    public void recordMappingDependencies() {
        for (Enumeration mappings = getDescriptor().getMappings().elements();
                 mappings.hasMoreElements();) {
            DatabaseMapping mapping = (DatabaseMapping)mappings.nextElement();
            if (mapping.isForeignReferenceMapping()) {
                if (((ForeignReferenceMapping)mapping).hasConstraintDependency()) {
                    Class ownedClass;
                    ClassDescriptor refDescriptor = ((ForeignReferenceMapping)mapping).getReferenceDescriptor();
                    if (refDescriptor == null) {
                        refDescriptor = session.getDescriptor(((ForeignReferenceMapping)mapping).getReferenceClass());
                    }
                    ownedClass = refDescriptor.getJavaClass();

                    if (ownedClass == null) {
                        throw oracle.toplink.essentials.exceptions.DescriptorException.referenceClassNotSpecified(mapping);
                    }
                    CommitOrderDependencyNode node = getOwner().nodeFor(ownedClass);
                    Vector ownedNodes = withAllSubclasses(node);

                    // I could remove duplicates here, but it's not that big a deal.
                    Helper.addAllToVector(relatedNodes, ownedNodes);
                } else if (((ForeignReferenceMapping)mapping).hasInverseConstraintDependency()) {
                    Class ownerClass;
                    ClassDescriptor refDescriptor = ((ForeignReferenceMapping)mapping).getReferenceDescriptor();
                    if (refDescriptor == null) {
                        refDescriptor = session.getDescriptor(((ForeignReferenceMapping)mapping).getReferenceClass());
                    }
                    ownerClass = refDescriptor.getJavaClass();
                    if (ownerClass == null) {
                        throw oracle.toplink.essentials.exceptions.DescriptorException.referenceClassNotSpecified(mapping);
                    }
                    CommitOrderDependencyNode ownerNode = getOwner().nodeFor(ownerClass);
                    Vector ownedNodes = withAllSubclasses(this);

                    // I could remove duplicates here, but it's not that big a deal.
                    Helper.addAllToVector(ownerNode.getRelatedNodes(), ownedNodes);
                }
            }
        }
    }

    /**
     * Add all owned classes for each descriptor through checking the mappings.
     * If I have a foreign mapping with a constraint dependency, then add it
     * If I'm related to a class, I'm related to all its subclasses and superclasses.
     * If my superclass is related to a class, I'm related to it.
     */
    public void recordSpecifiedDependencies() {
        for (Enumeration constraintsEnum = getDescriptor().getConstraintDependencies().elements();
                 constraintsEnum.hasMoreElements();) {
            Class ownedClass = (Class)constraintsEnum.nextElement();
            CommitOrderDependencyNode node = getOwner().nodeFor(ownedClass);
            Vector ownedNodes = withAllSubclasses(node);

            // I could remove duplicates here, but it's not that big a deal.
            Helper.addAllToVector(relatedNodes, ownedNodes);
        }
    }

    public void setDiscoveryTime(int time) {
        discoveryTime = time;
    }

    public void setFinishingTime(int time) {
        finishingTime = time;
    }

    public void setPredecessor(CommitOrderDependencyNode n) {
        predecessor = n;
    }

    public String toString() {
        if (descriptor == null) {
            return ToStringLocalization.buildMessage("empty_commit_order_dependency_node", (Object[])null);
        } else {
            Object[] args = { descriptor };
            return ToStringLocalization.buildMessage("node", args);
        }
    }

    public void visit() {
        //Visit this node as part of a topological sort
        int startTime;
        markInProgress();
        startTime = getOwner().getNextTime();
        setDiscoveryTime(startTime);

        for (Enumeration e = getRelatedNodes().elements(); e.hasMoreElements();) {
            CommitOrderDependencyNode node = (CommitOrderDependencyNode)e.nextElement();
            if (node.hasNotBeenVisited()) {
                node.setPredecessor(this);
                node.visit();
            }
            if (node.getPredecessor() == null) {
                node.setPredecessor(this);
            }
        }

        markVisited();
        setFinishingTime(getOwner().getNextTime());
    }

    // Return an enumeration of all mappings for my descriptor, including those inherited
    public Vector withAllSubclasses(CommitOrderDependencyNode node) {
        Vector results = new Vector();
        results.addElement(node);

        if (node.getDescriptor().hasInheritance()) {
            InheritancePolicy p = node.getDescriptor().getInheritancePolicy();

            // For bug 3019934 replace getChildDescriptors with getAllChildDescriptors.
            for (Enumeration e = p.getAllChildDescriptors().elements(); e.hasMoreElements();) {
                results.addElement(getOwner().nodeFor((ClassDescriptor)e.nextElement()));
            }
        }
        return results;
    }
}
