LinkDependencyGraph.java :  » Database-ORM » ODAL » com » completex » objective » components » persistency » core » impl » Java Open Source

Java Open Source » Database ORM » ODAL 
ODAL » com » completex » objective » components » persistency » core » impl » LinkDependencyGraph.java
/**
 *  Objective Database Abstraction Layer (ODAL)
 *  Copyright (c) 2004, The ODAL Development Group
 *  All rights reserved.
 *  For definition of the ODAL Development Group please refer to LICENCE.txt file
 *
 *  Distributable under LGPL license.
 *  See terms of license at gnu.org.
 */
package com.completex.objective.components.persistency.core.impl;

import com.completex.objective.components.persistency.AbstractPersistentObject;
import com.completex.objective.components.persistency.Cloneable;
import com.completex.objective.components.persistency.LifeCycleController;
import com.completex.objective.components.persistency.Link;
import com.completex.objective.components.persistency.OdalPersistencyException;
import com.completex.objective.components.persistency.OdalRuntimePersistencyException;
import com.completex.objective.components.persistency.PersistentObject;
import com.completex.objective.components.persistency.core.CircularDependencyError;
import com.completex.objective.components.persistency.type.Tracer;
import com.completex.objective.components.persistency.type.TracingCollection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collections;

/**
 * Class that determines order of inserts/updates/deletes/selects for complex persistent objects
 *
 * @author Gennady Krizhevsky
 */
public class LinkDependencyGraph implements Cloneable {

    private LinkHolder linkHolder;
    private boolean print = false;


    public LinkDependencyGraph() {
    }

    /**
     *
     * @param parent persistent object
     */
    public LinkDependencyGraph(AbstractPersistentObject parent) {
        assemble(parent);
    }

    /**
     * Assemble LinkDependencyGraph ordering links by dependecies
     *
     * @param parent
     */
    public void assemble(AbstractPersistentObject parent) {
        assemble(parent, false);
    }

    /**
     * Assemble LinkDependencyGraph ordering links by dependecies
     *
     * @param parent
     * @param reset indicator when set true re-assembling is performed
     */
    public void assemble(AbstractPersistentObject parent, boolean reset) {
        if (!reset && hasMetaAssembly()) {
            return;
        }
        Context context = new Context();
        assembleLocal(parent, context);
        assemblySecondPass(context);
    }

    /**
     * Get meta assembly Link by index
     * @param index
     * @return meta assembly Link
     */
    public Link getMetaAssemblyLink(int index) {
        return linkHolder == null ? null : linkHolder.getMetaAssemblyLink(index);
    }

    /**
     * 
     * @return metaAssemblySize
     */
    public int metaAssemblySize() {
        return linkHolder == null ? 0 : linkHolder.metaAssemblySize();
    }

    public boolean hasMetaAssembly() {
        return linkHolder != null && linkHolder.hasMetaAssembly();
    }

    private void assembleLocal(AbstractPersistentObject parent, Context context) {
        try {
            Link rootLink = parent.toLink().setCascadeAll();
            if (parent.hasChildren()) {
                assemble0(rootLink, parent.linkIterator(), context);
            }
            println(metaAssemblyToString(context.metaAssemblyMap));
            assembleOne(null, rootLink, context.metaAssemblyMap);
        } catch (StackOverflowError e) {
            String message = "\n";
            message += "   StackOverflowError is caught probably due to circular dependency, look at following current parent & child links which are probably circularly dependent: \n";
            message += "   currParentLink = " + context.currParentLink + "\n";
            message += "   currChildLink = " + context.currChildLink + "\n";
            throw new CircularDependencyError(message);
        }
    }

    private void assemble0(Link parentLink, LinkIterator it, Context context) {
        context.currParentLink = parentLink;
        for (; it.hasNext();) {
            Link childLink = it.nextLink();
            context.currChildLink = childLink;
            assemble007(parentLink, childLink, context);
        }
    }

    private void assemble007(Link parentLink, Link childLink, Context context) {
        assembleOne(parentLink, childLink, context.metaAssemblyMap);
        if (childLink.hasChildren()) {
            assemble0(childLink, childLink.linkIterator(), context);
        }
    }


    /**
     * Creates map keys of which are all the persistent objects in the graph,
     * values - List of objects the key object depends on
     *
     * @param parent
     */
    void assembleOne(Link parent, Link child, LinkedHashMap assembly) {
        if (child.isInsertBeforeParent()) {
            addDependant(parent, child, assembly);
        } else {
            addDependant(child, parent, assembly);
        }
    }

    private void addDependant(Link dependant, Link depender, LinkedHashMap assembly) {
        Set dependsOn = (Set) assembly.get(dependant);
        if (dependsOn == null) {
            dependsOn = new LinkedHashSet();
            println("Put dependant " + dependant);
            println("    Put depender " + depender);
            if (depender != null && !dependant.isCascadeDelete()) {
                // If dependant (for insert) is excluded from delete -
                // exclude the depender also: for deletes
                // the dependency order is opposite
                depender.setCascadeDelete(false);
            }

            if (depender != null && !depender.isCascadeInsert()) {
                dependant.setCascadeInsert(false);
            }

            if (depender != null && !depender.isCascadeUpdate()) {
                dependant.setCascadeUpdate(false);
            }

            assembly.put(dependant, dependsOn);
        }
        if (depender != null) {
            dependsOn.add(depender);
        }
    }

    //
    //
    // Assembly 2nd pass:
    //
    //

    /**
     * 1) Cycle by dependant
     * 2) find all the dependers
     * 3) cycle by dependers
     * 4) from each depender find if it is in the dependants
     * 5) if yes do 1)
     * 6) if not - add depender to final assembly
     * 7) add dependant to final assembly
     */
    private void assemblySecondPass(Context context) {
        LinkedHashSet finalAssemblySet = new LinkedHashSet();
        for (Iterator ait = context.metaAssemblyMap.keySet().iterator(); ait.hasNext();) {
            Link dependant = (Link) ait.next();
            assemblySecondPass0(dependant, finalAssemblySet, context.metaAssemblyMap, 0);
            finalAssemblySet.add(dependant);
        }

        ArrayList finalAssemblyList = new ArrayList(finalAssemblySet.size());
        for (Iterator it = finalAssemblySet.iterator(); it.hasNext();) {
            Link link = (Link) it.next();
            finalAssemblyList.add(link);
        }
        context.metaAssembly = new Link[finalAssemblyList.size()];
        finalAssemblyList.toArray(context.metaAssembly);
        for (int i = 0; i < context.metaAssembly.length; i++) {
            context.metaAssembly[i].setDependencyIndex(i);
        }
        linkHolder = new LinkHolder(context.metaAssembly);
    }

    private boolean assemblySecondPass0(Link dependant, LinkedHashSet finalAssemblySet, LinkedHashMap assembly, int level) {
        level++;
        boolean rc = false;
        Set dependers = (Set) assembly.get(dependant);
        if (dependers != null && dependers.size() > 0) {
            for (Iterator it = dependers.iterator(); it.hasNext();) {
                Link depender = (Link) it.next();
                println("depender[" + level + "] = " + depender);
                if (assembly.containsKey(depender)) {
                    rc = assemblySecondPass0(depender, finalAssemblySet, assembly, level);
                }
                if (!finalAssemblySet.contains(depender)) {
                    finalAssemblySet.add(depender);
                }
            }
        } else {
            finalAssemblySet.add(dependant);
            rc = true;
        }
        return rc;
    }

    //
    //
    // Walk is simple now -
    //
    //

    /**
     * @param reverseOrder - process tree from bottom to top 
     *  (as opposed to from top to bottom) - false for inserts/updates, true for deletes
     * @param visitor
     */
    public int walk(boolean reverseOrder, Visitor visitor) throws OdalPersistencyException {
        LinkAssemblyMeta meta = linkHolder.getMeta();

        //
        // calculate starting point of the graph for delete or insert:
        // it's determined by the break in cascade delete or insert:
        //
        boolean complexDirty = complexDirty(visitor);

        ArrayList[] finalAssemblies = getFinalAssemblies();
        ExtraParents extraParents = new ExtraParents();
        int rc = 0;
        Set visited = new LinkedHashSet();
        int size = finalAssemblies.length;
        for (int i = 0; i < size; i++) {
            int k = reverseOrder ? (size - i - 1) : i;
            ArrayList links = finalAssemblies[k];
            Link metaLink = meta.getLink(k);
            if (links != null) {
                boolean bypassForDelete = ModifyVisitorType.isDeleteType(visitor) && !metaLink.isCascadeDelete();
                boolean bypassForInsert = ModifyVisitorType.isInsertType(visitor) && !metaLink.isCascadeInsert();
                boolean bypassForUpdate = ModifyVisitorType.isUpdateType(visitor) && !metaLink.isCascadeUpdate();
                boolean bypass = (bypassForDelete || bypassForInsert || bypassForUpdate);
                if (!bypass) {
                    for (Iterator iterator = links.iterator(); iterator.hasNext();) {
                        Link link = (Link) iterator.next();
                        resetTracingOnInsertOrDelete(link, visitor);

                        //
                        // populate link with meta data:
                        //
                        link.setParentIndeces(metaLink.getParentIndeces());
                        link.setThisIndeces(metaLink.getThisIndeces());
                        try {

                            rc += walk007(link, visitor, visited, complexDirty, finalAssemblies, extraParents);
                        } catch (OdalPersistencyException e) {
                            throw e;
                        }
                    }
                }
            }
        }
        visited.clear();
        return rc;
    }

    private boolean complexDirty(Visitor visitor) {
        boolean complexDirty = false;
        if (visitor.getType() == ModifyVisitorType.UPDATE) {
            complexDirty = dirty();
        }
        return complexDirty;
    }

    private void resetTracingOnInsertOrDelete(Link link, Visitor visitor) {
        if (link != null && ModifyVisitorType.isInsertOrDelete(visitor) && link.hasChildren()) {
            for (LinkIterator it = link.linkIterator(); it.hasNext();) {
                Link childLink = it.nextLink();
                if (childLink != null && childLink.getResult() != null
                        && (childLink.getResult() instanceof TracingCollection)) {
                    TracingCollection tracingCollection = (TracingCollection) childLink.getResult();
                    tracingCollection.clearTrace();
                }
            }
        }
    }


    private ArrayList[] getFinalAssemblies() {
        LinkedHashMap finalAssemblyMap = linkHolder.getDataAssemblyMap();
        Collection collection = finalAssemblyMap.values();
        ArrayList[] finalAssemblies = new ArrayList[collection.size()];

        int m = 0;
        for (Iterator mapIterator = collection.iterator(); mapIterator.hasNext(); m++) {
            finalAssemblies[m] = (ArrayList) mapIterator.next();
            for (int i = 0; i < finalAssemblies[m].size(); i++) {
                Link link = (Link) finalAssemblies[m].get(i);
                if (link != null) {
                    link.setDependencyIndex(m);
                }

            }
        }
        return finalAssemblies;
    }


    private int walk007(Link link, Visitor visitor, Set visited, boolean complexDirty, ArrayList[] finalAssemblies, ExtraParents extraParents) throws OdalPersistencyException {
        int rc = 0;
        Object result = link.getResult();
        LifeCycleController controller = link.getLifeCycleController();
        if (result != null) {
            if (result instanceof AbstractPersistentObject) {
                rc += visit0((AbstractPersistentObject) result, visitor, visited, controller, link, complexDirty, finalAssemblies, extraParents);
            } else if (result instanceof TracingCollection) {
                //
                // If this is tracing collections then operation is generalixzed update.
                // To exclude those entries that has been deleted or just inserted -
                // check if the entry is traced. If so - it is deleted or inserted - skip it:
                //
                TracingCollection list = (TracingCollection) result;
                for (Iterator iterator = list.iterator(); iterator.hasNext();) {
                    PersistentObject persistentObject = (PersistentObject) iterator.next();
                    if (!list.containsEntry(persistentObject)) {
                        rc += visit0(persistentObject, visitor, visited, controller, link, complexDirty, finalAssemblies, extraParents);
                    }
                }
            } else if (result instanceof Collection) {
                Collection list = (Collection) result;
                for (Iterator iterator = list.iterator(); iterator.hasNext();) {
                    AbstractPersistentObject persistentObject = (AbstractPersistentObject) iterator.next();
                    rc += visit0(persistentObject, visitor, visited, controller, link, complexDirty, finalAssemblies, extraParents);
                }
            } else {
                throw new IllegalArgumentException("Unknown type " + result.getClass().getName());
            }

        }
        return rc;
    }

    private int visit0(AbstractPersistentObject persistent, Visitor visitor, Set visited, LifeCycleController controller,
                       Link link, boolean complexDirty, ArrayList[] finalAssemblies, ExtraParents extraParents) throws OdalPersistencyException {
        int rc = 0;
        if (!visited.contains(persistent)) {
            insertDependentIndices(visitor, link, (PersistentObject) persistent, finalAssemblies, extraParents);
            rc = visitor.visit(persistent, controller, persistent == visitor.getRoot(), complexDirty);
            visited.add(persistent);
        }
        return rc;
    }


    /**
     * Populate indeces in dependent object if there are no values. it will take care of autogenerated parent
     * fields - children's ones will be populated automatically
     *
     * @param visitor
     * @param link
     * @param persistent
     * @param finalAssemblies
     * @param extraParents
     */
    private void insertDependentIndices(Visitor visitor, Link link, PersistentObject persistent, ArrayList[] finalAssemblies, ExtraParents extraParents) {
        if (visitor.getType() == ModifyVisitorType.INSERT) {
            if (link.getParentLink() != null
                    && link.getDependencyIndex() > 0
                    && link.getParentIndeces() != null
                    && link.getParentLink().getResult() != null) {
                insertDependentIndicesDirect(link, persistent, finalAssemblies, extraParents);
            } else { // Case of reversed dependency - slave is root object ...
                insertDependentIndicesReversed(persistent, link, extraParents);
            }
        }
    }

    private void insertDependentIndicesReversed(PersistentObject persistent, Link link, ExtraParents extraParents) {
        if (persistent != null && persistent.toLink() != null
                && persistent.toLink().getResult() instanceof PersistentObject) {
            Link parentLink = ((PersistentObject) persistent.toLink().getResult()).toLink();
            if (parentLink.hasChildren()) {
                for (LinkIterator it = parentLink.linkIterator(); it.hasNext();) {
                    Link childLink = it.nextLink();
                    if (childLink.getDependencyIndex() < link.getDependencyIndex()) {
                        Object result = childLink.getResult();
                        PersistentObject childPersistentObject = null;
                        // It can be collection of size 1:
                        if (result instanceof Collection) {
                            Collection collection = (Collection) result;
                            if (!collection.isEmpty()) {
                                childPersistentObject = (PersistentObject) collection.iterator().next();
                            }
                        } else if (result instanceof PersistentObject) {
                            childPersistentObject = ((PersistentObject) result);
                        } else if (result != null) {
                            throw new OdalRuntimePersistencyException("Unsupported result type " + result);
                        }
                        if (childPersistentObject != null) {
                            int[] parentIndeces = childPersistentObject.toLink().getParentIndeces();
                            int[] thisIndeces = childPersistentObject.toLink().getThisIndeces();

                            for (int i = 0; i < thisIndeces.length; i++) {
                                int thisIndex = thisIndeces[i];
                                int parentIndex = parentIndeces[i];
                                persistent.record().setObject(parentIndex, childPersistentObject.record().getObject(thisIndex));
                            }
                        }
                    }
                }
            }
        }
    }

    private void insertDependentIndicesDirect(Link link, PersistentObject persistent, ArrayList[] finalAssemblies, ExtraParents extraParents) {
        if (link.getParentLink().getResult() instanceof PersistentObject) {
            Link parentLink = link.getParentLink();
            PersistentObject parentObject = (PersistentObject) parentLink.getResult();
            insertIndicesOne(link, persistent, parentObject);
            // New stuff to populate extra indices
            insertExtraDependentIndicesDirect(parentLink, finalAssemblies, link, persistent, extraParents);
        }

        //
        // Insert extra parent indices:
        //
        List extraParentLinks = extraParents.getExtraParents(link);
        for (int i = 0; i < extraParentLinks.size(); i++) {
            Link parentLink = (Link) extraParentLinks.get(i);
            if (parentLink.getResult() instanceof PersistentObject) {
                PersistentObject parentObject = (PersistentObject) parentLink.getResult();
                insertIndicesOne(parentLink, parentObject, persistent);
            }
        }
    }

    private void insertExtraDependentIndicesDirect(Link parentLink, ArrayList[] finalAssemblies, Link link, PersistentObject persistent, ExtraParents extraParents) {
        int dependencyIndex = parentLink.getDependencyIndex();
        ArrayList links = finalAssemblies[dependencyIndex];
        for (int i = 0; i < links.size(); i++) {
            Link dependentLink = (Link) links.get(i);
            if (dependentLink.hasChildren() && dependentLink.getResult() instanceof PersistentObject) {
                PersistentObject extraParentObject = (PersistentObject) dependentLink.getResult();
                for (LinkIterator it = dependentLink.linkIterator(); it.hasNext();) {
                    Link extraParentLink = it.nextLink();
                    if (extraParentLink != link.getParentLink() && extraParentLink.getResult() == persistent) {
//                        insertIndicesOne(link, persistent, extraParentObject);
                        if (link.getResult() != null) {
                            extraParents.addExtraParent(dependentLink, link);
                        }
                    }
                }
            }

        }
    }


    private void insertIndicesOne(Link link, PersistentObject childObject, PersistentObject parentObject) {
        setDependentKeys(link, childObject, parentObject);
    }

    /**
     * Utility method that set dependent values from child to parent or from parent to child PersistentObject
     * based on Link.isInsertBeforeParent(), Link.getDependencyIndex(), Link.getThisIndeces() != null,
     * Link.getParentIndeces() methods. Dependent values usually
     * those based on primary/foreign keys relations
     *
     * @param link one of the parentObject links establishing relationship to childObject
     * @param childObject
     * @param parentObject
     */
    public static void setDependentKeys(Link link, PersistentObject childObject, PersistentObject parentObject) {
        if (link.getThisIndeces() != null && link.getParentIndeces() != null) {
            for (int i = 0; i < link.getParentIndeces().length; i++) {
                int childIndex = link.getThisIndeces()[i];
                int parentIndex = link.getParentIndeces()[i];
                if (!link.isInsertBeforeParent() && link.getDependencyIndex() >
                        link.getParentLink().getDependencyIndex()) {
                    setDependentIndex(parentObject, parentIndex, childObject, childIndex);
                } else {
                    setDependentIndex(childObject, childIndex, parentObject, parentIndex);
                }
            }
        }
    }

    static void setDependentIndex(PersistentObject parentObject, int parentIndex, PersistentObject persistentObject, int thisIndex) {
        Object parentValue = parentObject.record().getObject(parentIndex);
        persistentObject.record().setObject(thisIndex, parentValue);
    }

    public String toString() {
        return super.toString() +
                ": {\n" + metaAssemblyToString() + "}";
    }

    /**
     * 
     * @param metaAssemblyMap
     * @return metaAssemblyMap as String
     */
    public String metaAssemblyToString(LinkedHashMap metaAssemblyMap) {
        if (metaAssemblyMap == null) {
            return "null";
        }
        String indent = "    ";
        StringBuffer buffer = new StringBuffer();
        int count = 0;
        for (Iterator it = metaAssemblyMap.keySet().iterator(); it.hasNext();) {
            Object key = it.next();
            Object value = metaAssemblyMap.get(key);
            buffer.append(indent).append("Assembly line[").append(++count).append("]; <dependant> = ").append(key)
                    .append("; <dependsOn> = ").append(value).append("\n");
        }
        return buffer.toString();
    }

    /**
     * 
     * @return meta assembly as string
     */
    public String metaAssemblyToString() {
        return linkHolder.metaAssemblyToString();
    }

    private void println(String s) {
        if (print) {
            System.err.println(s);
        }
    }

    /**
     * 
     * @return copy of this object
     * @throws CloneNotSupportedException
     */
    public Object clone() throws CloneNotSupportedException {
        LinkDependencyGraph graph = (LinkDependencyGraph) super.clone();
        graph.linkHolder = (LinkHolder) linkHolder.clone();
        return graph;
    }

    /**
     * Add link addToAssembly at specific path
     * 
     * @param linkPath
     * @param link
     */
    public void addToAssembly(String linkPath, Link link) {
        linkHolder.addLink(linkPath, link);
    }

    /**
     * Traverses the dependecy graph and determines if any of NON-BYPASSED link entries is dirty
     *
     * @return true if at least one of non-bypassed entries is dirty
     */
    public boolean dirty() {
        LinkedHashMap map = linkHolder.getDataAssemblyMap();
        for (Iterator it = map.keySet().iterator(); it.hasNext();) {
            String key = (String) it.next();
            List links = (List) map.get(key);
            for (Iterator iterator = links.iterator(); iterator.hasNext();) {
                Link link = (Link) iterator.next();
                Link metaLink = linkHolder.getMeta().getLink(key);
                boolean bypassForDelete = !metaLink.isCascadeDelete();
                boolean bypassForInsert = !metaLink.isCascadeInsert();
                boolean bypassForUpdate = !metaLink.isCascadeUpdate();
                boolean bypass = bypassForDelete && bypassForInsert && bypassForUpdate;
                if (!bypass) {
                    Object result = link.getResult();
                    if (result != null && result instanceof AbstractPersistentObject && !bypassForUpdate) {
                        if (((AbstractPersistentObject) result).dirty()) {
                            return true;
                        }
                    } else if (result instanceof TracingCollection) {
                        TracingCollection tracingCollection = (TracingCollection) result;
                        int [] ops = tracingCollection.performedOperations();
                        bypassForDelete = Tracer.Util.isDeleteOnly(ops) && bypassForDelete;
                        bypassForInsert = Tracer.Util.isInsertOnly(ops) && bypassForInsert;
                        bypassForUpdate = Tracer.Util.isUpdateOnly(ops) && bypassForUpdate;
                        bypass = bypassForDelete || bypassForInsert || bypassForUpdate;
                        if (!bypass && tracingCollection.dirty()) {
                            return true;
                        }
                    } else if (result instanceof Collection && !bypassForUpdate) {
                        Collection collection = (Collection) result;
                        for (Iterator iterator1 = collection.iterator(); iterator1.hasNext();) {
                            Object o1 = iterator1.next();
                            if (((AbstractPersistentObject) o1).dirty()) {
                                return true;
                            }
                        }
                    }
                }
            }
        }

        return false;
    }

    //
    //
    //  Util classes:
    //
    //

    /**
     * Modify Visitor Type holder
     */
    public static class ModifyVisitorType {

        private String name;

        public ModifyVisitorType(String name) {
            this.name = name;
        }

        public static final ModifyVisitorType NULL = new ModifyVisitorType("NULL");
        public static final ModifyVisitorType INSERT = new ModifyVisitorType("INSERT");
        public static final ModifyVisitorType UPDATE = new ModifyVisitorType("UPDATE");
        public static final ModifyVisitorType DELETE = new ModifyVisitorType("DELETE");

        public String toString() {
            return name;
        }

        public static boolean isNullType(Visitor visitor) {
            return visitor.getType() == NULL;
        }

        public static boolean isInsertType(Visitor visitor) {
            return visitor.getType() == INSERT;
        }

        public static boolean isUpdateType(Visitor visitor) {
            return visitor.getType() == UPDATE;
        }

        public static boolean isDeleteType(Visitor visitor) {
            return visitor.getType() == DELETE;
        }

        public static boolean isInsertOrDelete(Visitor visitor) {
            return isInsertType(visitor) || isDeleteType(visitor);
        }
    }

    /**
     * Interface to traverse AbstractPersistentObject trees 
     */
    public static interface Visitor {
        /**
         * 
         * @param persistentObject
         * @param controller
         * @param root set true if persistentObject parameter is the root of traversed tree
         * @param complexDirty set true if the object tree is "dirty" in generalized sense
         * @return indicator of how many objects are processed
         * @throws OdalPersistencyException
         */
        int visit(AbstractPersistentObject persistentObject,
                  LifeCycleController controller,
                  boolean root,
                  boolean complexDirty) throws OdalPersistencyException;

        /**
         * @see ModifyVisitorType
         * 
         * @return ModifyVisitorType
         */
        ModifyVisitorType getType();

        /**
         * 
         * @return root object
         */
        PersistentObject getRoot();
    }

    private static class Context {
        private Link currParentLink;
        private Link currChildLink;
        private LinkedHashMap metaAssemblyMap = new LinkedHashMap();
        private Link[] metaAssembly;
    }

    private static class ExtraParents {
        private LinkedHashMap extraParents; /* LinkedHashMap<Link, List<Link>> */

        public void addExtraParent(Link child, Link extraParent) {
            List parents = (List) lazyExtraParents().get(child);
            if (parents == null) {
                parents = new ArrayList();
                lazyExtraParents().put(child, parents);
            }
            parents.add(extraParent);
        }

        private Map lazyExtraParents() {
            if (extraParents == null) {
                extraParents = new LinkedHashMap();
            }
            return extraParents;
        }

        public List getExtraParents(Link child) {
            List result = null;
            if (extraParents != null) {
                result = (List) extraParents.get(child);
            }

            return result == null ? Collections.EMPTY_LIST : result;
        }
    }

    /**
     * Contains Link Assembly Meta data and dataAssemblyMap - 
     * map of links lists with slink path as a key
     */
    static class LinkHolder implements Cloneable {

        private LinkedHashMap dataAssemblyMap; /* LinkedHashMap<String, List<Link>> */
        private LinkAssemblyMeta meta;

        LinkHolder(Link[] metaAssembly) {
            this.meta = new LinkAssemblyMeta(metaAssembly);
            setDataAssemblyMap(this);
        }

        private LinkHolder() {
        }

        private LinkHolder newLinkHolder() {
            LinkHolder holder = new LinkHolder();
            holder.meta = meta;
            setDataAssemblyMap(holder);
            return holder;
        }

        /**
         * 
         * @return dataAssemblyMap
         */
        public LinkedHashMap getDataAssemblyMap() {
            return dataAssemblyMap;
        }

        private void setDataAssemblyMap(LinkHolder holder) {
            holder.dataAssemblyMap = meta.newDataAssemblyMap();
        }

        /**
         * Add Link to dataAssemblyMap
         * 
         * @param linkPath
         * @param link
         */
        public void addLink(String linkPath, Link link) {
            if (!link.isAdHoc()) {
                List links = (List) dataAssemblyMap.get(linkPath);
                if (links == null) {
                    throw new OdalRuntimePersistencyException("Cannot get links by linkPath " + linkPath);
                }
                links.add(link);
            }
        }

        /**
         * 
         * @return copy of LinkHolder
         * @throws CloneNotSupportedException
         */
        public Object clone() throws CloneNotSupportedException {
            return newLinkHolder();
        }

        /**
         * 
         * @return Link assembly meta data
         */
        public LinkAssemblyMeta getMeta() {
            return meta;
        }

        /**
         * 
         * @return metaAssemblySize
         */
        public int metaAssemblySize() {
            return meta == null ? 0 : meta.size();
        }

        /**
         * 
         * @return true if LinkHolder meta assembly is empty
         */
        public boolean isMetaAssemblyEmpty() {
            return metaAssemblySize() == 0;
        }

        /**
         * 
         * @return true if LinkHolder has meta assembly
         */
        public boolean hasMetaAssembly() {
            return !isMetaAssemblyEmpty();
        }

        public String toString() {
            return super.toString() +
                    "{" +
                    dataAssemblyMap +
                    "}";
        }

        public String metaAssemblyToString() {
            if (isMetaAssemblyEmpty()) {
                return "null";
            }
            String indent = "    ";
            StringBuffer buffer = new StringBuffer();
            int count = 0;

            for (int i = 0; i < metaAssemblySize(); i++) {
                Link key = meta.getLink(i);
                buffer.append(indent).append("Final Assembly line[").append(++count).append("]; <link> = ").append(key)
                        .append("\n");
            }
            return buffer.toString();
        }

        public Link getMetaAssemblyLink(int index) {
            return meta == null ? null : meta.getLink(index);
        }
    }

    /**
     * Link Assembly Meta data. Contains link array and map of links with path as key and
     * Link as value 
     */
    static class LinkAssemblyMeta {
        private Map metaAssemblyMap;
        private Link[] metaAssembly;

        /**
         * 
         * @param metaAssembly
         */
        public LinkAssemblyMeta(Link[] metaAssembly) {
            this.metaAssembly = metaAssembly;
            this.metaAssemblyMap = newMetaAssemblyMap(metaAssembly);
        }

        private Map newMetaAssemblyMap(Link[] finalAssembly) {
            LinkedHashMap finalAssemblyMap = new LinkedHashMap(finalAssembly.length);
            for (int i = 0; i < finalAssembly.length; i++) {
                finalAssembly[i].setDependencyIndex(i);
                finalAssemblyMap.put(finalAssembly[i].getPathString(), finalAssembly[i]);
            }
            return finalAssemblyMap;
        }

        public Link getLink(int index) {
            return metaAssembly[index];
        }

        public Link getLink(String path) {
            return (Link) metaAssemblyMap.get(path);
        }

        public int size() {
            return metaAssembly != null ? metaAssembly.length : 0;
        }

        /**
         * Creates new Data Assembly Map which has Link path as a key 
         * and empty List as value. The list gets populated in  
         * LinkDependencyGraph.addToAssembly() method
         * 
         * @return new Data Assembly Map
         */
        public LinkedHashMap newDataAssemblyMap() {
            LinkedHashMap dataAssemblyMap = new LinkedHashMap(metaAssembly.length);
            for (int i = 0; i < metaAssembly.length; i++) {
                dataAssemblyMap.put(metaAssembly[i].getPathString(), new ArrayList());
            }
            return dataAssemblyMap;
        }

        public String toString() {
            return super.toString();
        }

    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.