/******************************************************************************* * Copyright (c) 2006, 2011 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation * Ericsson - Modified for additional features in DSF Reference implementation * Dobrin Alexiev (Texas Instruments) - added helpers for recursive data model contexts (bug 240208) *******************************************************************************/ package org.eclipse.cdt.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.cdt.dsf.concurrent.ThreadSafe; /** * Holder for utility static methods for manipulating IDMContext objects. * * @since 1.0 */ 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. 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. */ @ThreadSafe @SuppressWarnings("unchecked") public static <V extends IDMContext> V getAncestorOfType(IDMContext ctx, Class<V> ancestorType) { if(ctx == null) return null; // Check the first context here for efficiency if (ancestorType.isAssignableFrom(ctx.getClass())) { return (V)ctx; } // Use a LinkedHashSet to avoid duplicates and preserver insertion-order Set<IDMContext> nodes = new LinkedHashSet<IDMContext>(); nodes.addAll(Arrays.asList(ctx.getParents())); while (nodes.isEmpty() == false) { Set<IDMContext> parents = nodes; nodes = new LinkedHashSet<IDMContext>(); for (IDMContext parent : parents) { if (ancestorType.isAssignableFrom(parent.getClass())) { return (V)parent; } nodes.addAll(Arrays.asList(parent.getParents())); } } return null; } /** * Finds the top most ancestor of the specified type. * It assumes only one immediate parent of the give type exists. * The search is done until there is no more immediate parents of the given type. * The method returns the last one found. * * @param <V> * @param ctx DMC to search. * @param ancestorType Class type of the desired DMC ancestor. * @return Returns the ancestor if found, null otherwise. * @since 2.2 */ @ThreadSafe @SuppressWarnings("unchecked") public static <V extends IDMContext> V getTopMostAncestorOfType(IDMContext ctx, Class<V> ancestorType) { if(ctx == null) return null; V topMostAncestor = null; boolean hasAncestorOfType = false; IDMContext current = ctx; do { hasAncestorOfType = false; IDMContext[] parents = current.getParents(); for( IDMContext parent : parents) { if (ancestorType.isAssignableFrom(parent.getClass())) { hasAncestorOfType = true; topMostAncestor = (V) parent; current = parent; } } } while( hasAncestorOfType); return topMostAncestor; } /** * Finds the immediate parent of the specified type if exists. * * @param ctx DMC to search. * @param ancestorType Class type of the desired DMC ancestor. * @return Returns the ancestor if found, null otherwise. * @since 2.2 */ @ThreadSafe @SuppressWarnings("unchecked") public static <V extends IDMContext> V getParentOfType(IDMContext ctx, Class<V> ancestorType) { if(ctx == null) return null; for( IDMContext parent : ctx.getParents()) if (ancestorType.isAssignableFrom(parent.getClass())) return (V)parent; 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. * @since 1.1 */ @ThreadSafe @SuppressWarnings("unchecked") public static <V extends IDMContext> V[] getAllAncestorsOfType(IDMContext ctx, Class<V> ancestorType) { if(ctx == null) return null; // Use a LinkedHashSet to avoid duplicates and preserver insertion-order Set<V> requestedAncestors = new LinkedHashSet<V>(); Set<IDMContext> nodes = new LinkedHashSet<IDMContext>(); nodes.add(ctx); while (nodes.isEmpty() == false) { Set<IDMContext> parents = nodes; nodes = new LinkedHashSet<IDMContext>(); 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 * potentialAncestor is in fact an ancestor. * @param dmc DM Contexts who's ancestors to check. * @param potentialAncestor Ancestor context to look for. * @return true if a match is found. */ @ThreadSafe public static boolean isAncestorOf(IDMContext dmc, IDMContext potentialAncestor) { // Check the direct parents for a match. for (IDMContext parentDmc : dmc.getParents()) { if (potentialAncestor.equals(parentDmc)) { return true; } } // Recursively check the parents' parents for a match. for (IDMContext parentDmc : dmc.getParents()) { if (isAncestorOf(parentDmc, potentialAncestor)) { return true; } } // No match. return false; } /** * Traverses all the parents of a context and converts the whole * into a list. */ @ThreadSafe public static List<IDMContext> toList(IDMContext dmc) { /* * This method is implemented recursively, which is not necessarily * the most efficient way to do this. */ List<IDMContext> list = new ArrayList<IDMContext>(); list.add(dmc); for (IDMContext parentDmc : dmc.getParents()) { list.addAll(toList(parentDmc)); } return list; } }