diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/DMContexts.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/DMContexts.java index 46400e17a5a..54306ec86d4 100644 --- a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/DMContexts.java +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/datamodel/DMContexts.java @@ -11,8 +11,12 @@ *******************************************************************************/ package org.eclipse.dd.dsf.datamodel; +import java.lang.reflect.Array; import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import org.eclipse.dd.dsf.concurrent.ThreadSafe; @@ -26,10 +30,15 @@ public class DMContexts { * Convenience constant. */ public static final IDMContext[] EMPTY_CONTEXTS_ARRAY = new IDMContext[0]; - + /** * Finds a data model context of given type among ancestors of the - * specified context. + * specified context. The returned ancestor is the one closest to the + * specified context, in terms of depth. + * + * Note that for efficiency, this method does not re-use getAllAncestorsOfType() + * to avoid the unnecessary creation of an array. + * * @param ctx DMC to search. * @param ancestorType Class type of the desired DMC ancestor. * @return Returns the ancestor if found, null otherwise. @@ -39,22 +48,65 @@ public class DMContexts { public static V getAncestorOfType(IDMContext ctx, Class ancestorType) { if(ctx == null) return null; + // Check the first context here for efficiency if (ancestorType.isAssignableFrom(ctx.getClass())) { return (V)ctx; } - for (IDMContext parent : ctx.getParents()) { - if (ancestorType.isAssignableFrom(parent.getClass())) { - return (V)parent; - } - } + // Use a LinkedHashSet to avoid duplicates and preserver insertion-order + Set nodes = new LinkedHashSet(); + nodes.addAll(Arrays.asList(ctx.getParents())); + while (nodes.isEmpty() == false) { + Set parents = nodes; + nodes = new LinkedHashSet(); + for (IDMContext parent : parents) { + if (ancestorType.isAssignableFrom(parent.getClass())) { + return (V)parent; + } - for (IDMContext parent : ctx.getParents()) { - V ancestor = getAncestorOfType(parent, ancestorType); - if (ancestor != null) return ancestor; + nodes.addAll(Arrays.asList(parent.getParents())); + } } + return null; } + + /** + * Finds all data model contexts of given type among ancestors of the + * specified context. Ancestors are returned in order of closest to farthest, + * in terms of depth. + * @param ctx DMC to search. + * @param ancestorType Class type of the desired DMC ancestor. + * @return Returns all ancestors found, null if none. + */ + @ThreadSafe + @SuppressWarnings("unchecked") + public static V[] getAllAncestorsOfType(IDMContext ctx, Class ancestorType) { + if(ctx == null) + return null; + + // Use a LinkedHashSet to avoid duplicates and preserver insertion-order + Set requestedAncestors = new LinkedHashSet(); + Set nodes = new LinkedHashSet(); + nodes.add(ctx); + while (nodes.isEmpty() == false) { + Set parents = nodes; + nodes = new LinkedHashSet(); + for (IDMContext parent : parents) { + if (ancestorType.isAssignableFrom(parent.getClass())) { + requestedAncestors.add((V)parent); + } + + nodes.addAll(Arrays.asList(parent.getParents())); + } + } + + if (requestedAncestors.isEmpty()) return null; + else { + V[] v = (V[])Array.newInstance(ancestorType, 0); + return requestedAncestors.toArray(v); + } + } /** * Checks all ancestors for a given context to see if the given