/**
* 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();
}
}
}
|