/******************************************************************************* * Copyright (c) 2005, 2011 IBM Corporation 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: * IBM Corporation - initial API and implementation * QNX Software System *******************************************************************************/ package org.eclipse.cdt.internal.ui; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.ui.model.IWorkbenchAdapter; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.IArchive; import org.eclipse.cdt.core.model.IArchiveContainer; import org.eclipse.cdt.core.model.IBinary; import org.eclipse.cdt.core.model.IBinaryContainer; import org.eclipse.cdt.core.model.IBinaryModule; import org.eclipse.cdt.core.model.ICContainer; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICModel; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.IInclude; import org.eclipse.cdt.core.model.IMacro; import org.eclipse.cdt.core.model.IMember; import org.eclipse.cdt.core.model.INamespace; import org.eclipse.cdt.core.model.IParent; import org.eclipse.cdt.core.model.ISourceReference; import org.eclipse.cdt.core.model.ISourceRoot; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.model.IWorkingCopy; import org.eclipse.cdt.ui.CDTUITools; import org.eclipse.cdt.ui.CElementGrouping; import org.eclipse.cdt.ui.IncludesGrouping; import org.eclipse.cdt.ui.NamespacesGrouping; /** * A base content provider for C elements. It provides access to the * C element hierarchy without listening to changes in the C model. * Use this class when you want to present the C elements * in a modal dialog or wizard. * <p> * The following C element hierarchy is surfaced by this content provider: * <p> * <pre> C model (<code>ICModel</code>)<br> C project (<code>ICProject</code>)<br> Source root (<code>ISourceRoot</code>)<br> C Container(folders) (<code>ICContainer</code>)<br> Translation unit (<code>ITranslationUnit</code>)<br> Binary file (<code>IBinary</code>)<br> Archive file (<code>IArchive</code>)<br> Non C Resource file (<code>Object</code>)<br> * </pre> */ public class BaseCElementContentProvider implements ITreeContentProvider { protected static final Object[] NO_CHILDREN= new Object[0]; protected boolean fProvideMembers= false; protected boolean fProvideWorkingCopy= false; protected boolean fIncludesGrouping= false; protected boolean fNamespacesGrouping= false; protected boolean fMemberGrouping= false; protected boolean fMacroGrouping= false; public BaseCElementContentProvider() { this(false, false); } public BaseCElementContentProvider(boolean provideMembers, boolean provideWorkingCopy) { fProvideMembers= provideMembers; fProvideWorkingCopy= provideWorkingCopy; } /** * Returns whether the members are provided when asking * for a TU's children. */ public boolean getProvideMembers() { return fProvideMembers; } /** * Returns whether the members are provided when asking * for a TU's children. */ public void setProvideMembers(boolean b) { fProvideMembers= b; } /** * Sets whether the members are provided from * a working copy of a compilation unit */ public void setProvideWorkingCopy(boolean b) { fProvideWorkingCopy= b; } /** * Returns whether the members are provided * from a working copy a compilation unit. */ public boolean getProvideWorkingCopy() { return fProvideWorkingCopy; } /** * Can elements be group. */ public boolean areIncludesGroup() { return fIncludesGrouping; } /** * Allow Elements to be group. * @param b */ public void setIncludesGrouping(boolean b) { fIncludesGrouping = b; } /** * Can elements be group. */ public boolean areNamespacesGroup() { return fNamespacesGrouping; } /** * Allow Elements to be group. * @param b */ public void setNamespacesGrouping(boolean b) { fNamespacesGrouping = b; } /** * @return whether grouping of members is enabled */ public boolean isMemberGroupingEnabled() { return fMemberGrouping; } /** * Enable/disable member grouping by common namespace. * @param enable */ public void setMemberGrouping(boolean enable) { fMemberGrouping = enable; } /** * @return whether grouping of macros is enabled */ public boolean isMacroGroupingEnabled() { return fMacroGrouping; } /** * Enable/disable marco grouping * @param enable */ public void setMacroGrouping(boolean enable) { fMacroGrouping = enable; } /* (non-Cdoc) * Method declared on IContentProvider. */ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } /* (non-Cdoc) * Method declared on IContentProvider. */ public void dispose() { } /* (non-Cdoc) * Method declared on IStructuredContentProvider. */ public Object[] getElements(Object parent) { return getChildren(parent); } /* (non-Cdoc) * Method declared on ITreeContentProvider. */ public Object[] getChildren(Object element) { if (!exists(element)) return NO_CHILDREN; try { if (element instanceof ICModel) { return getCProjects((ICModel)element); } else if (element instanceof ICProject ) { return getSourceRoots((ICProject)element); } else if (element instanceof ICContainer) { return getCResources((ICContainer)element); } else if (element instanceof ITranslationUnit) { // if we want to get the children of a translation unit if (fProvideMembers) { // if we want to use the working copy of it ITranslationUnit tu = (ITranslationUnit)element; if (fProvideWorkingCopy){ // if it is not already a working copy if (!(element instanceof IWorkingCopy)){ // if it has a valid working copy IWorkingCopy copy = CDTUITools.getWorkingCopyManager().findSharedWorkingCopy(tu); if (copy != null) { tu = copy; } } } return getTranslationUnitChildren(tu); } } else if (element instanceof IBinary) { return ((IBinary)element).getChildren(); } else if (element instanceof IArchive) { return ((IArchive)element).getChildren(); } else if (element instanceof IBinaryModule) { return ((IBinaryModule)element).getChildren(); } else if (element instanceof INamespace) { return getNamespaceChildren((INamespace) element); } else if (element instanceof ISourceReference && element instanceof IParent) { return ((IParent)element).getChildren(); } else if (element instanceof IProject) { return getResources((IProject)element); } else if (element instanceof IFolder) { return getResources((IFolder)element); } else if (element instanceof CElementGrouping) { return ((CElementGrouping)element).getChildren(element); } } catch (CModelException e) { //CUIPlugin.log(e); return NO_CHILDREN; } return NO_CHILDREN; } /* (non-Cdoc) * * @see ITreeContentProvider */ public boolean hasChildren(Object element) { if (fProvideMembers) { // assume TUs and binary files are never empty if (element instanceof IBinary || element instanceof ITranslationUnit || element instanceof IArchive) { return true; } } else { // don't allow to drill down into a compilation unit or class file if (element instanceof ITranslationUnit || element instanceof IBinary || element instanceof IArchive || element instanceof IFile) { return false; } } if (element instanceof ICProject) { ICProject cp= (ICProject)element; if (!cp.getProject().isOpen()) { return false; } return true; } if (element instanceof ICContainer) { ICContainer container= (ICContainer)element; IResource resource= container.getResource(); if (resource instanceof IContainer) { try { return ((IContainer)resource).members().length > 0; } catch (CoreException exc) { return false; } } } if (element instanceof IParent) { // when we have C children return true, else we fetch all the children if (((IParent)element).hasChildren()) { return true; } } if (element instanceof CElementGrouping) { return true; } Object[] children= getChildren(element); return (children != null) && children.length > 0; } /* (non-Cdoc) * Method declared on ITreeContentProvider. */ public Object getParent(Object element) { if (!exists(element)) { return null; } return internalGetParent(element); } public Object internalGetParent(Object element) { if (element instanceof IResource) { IResource parent= ((IResource)element).getParent(); if (parent != null && parent.isAccessible()) { ICElement cParent= CoreModel.getDefault().create(parent); if (cParent != null && cParent.exists()) { return cParent; } } return parent; } Object parent = null; if (element instanceof ICElement) { if (element instanceof ISourceRoot && !CCorePlugin.showSourceRootsAtTopOfProject()) { parent = ((ICElement) element).getResource().getParent(); if (parent instanceof IProject) { return ((ICElement) element).getCProject(); } } else parent = ((ICElement)element).getParent(); // translate working copy parent to original TU, // because working copies are never returned by getChildren // this is necessary for proper show-in-target support if (parent instanceof IWorkingCopy) { parent= ((IWorkingCopy)parent).getOriginalElement(); } } else if (element instanceof IWorkbenchAdapter) { parent = ((IWorkbenchAdapter)element).getParent(element); } // if the parent is the default ISourceRoot == ICProject return the project if (parent instanceof ISourceRoot) { if (isProjectSourceRoot((ISourceRoot)parent)) { parent = ((ISourceRoot)parent).getCProject(); } } else if (parent instanceof IBinaryContainer || parent instanceof IArchiveContainer) { // If the virtual container is the parent we must find the legitimate parent. if (element instanceof ICElement) { IResource res = ((ICElement)element).getResource(); if (res != null) { parent = internalGetParent(res); } } } if (parent instanceof INamespace && fNamespacesGrouping) { final INamespace namespace = (INamespace)parent; final NamespacesGrouping grouping = new NamespacesGrouping(namespace.getTranslationUnit(), namespace, fMemberGrouping); if (grouping.getNamespaces().length > 2) { parent = grouping; } } if (parent instanceof IMember && fMemberGrouping) { final IMember member = (IMember)parent; final String ns = getElementNamespace(member); if (ns != null) { Object parentParent = member.getParent(); if (parentParent instanceof INamespace && fNamespacesGrouping) { final INamespace namespace = (INamespace)parent; final NamespacesGrouping grouping = new NamespacesGrouping(namespace.getTranslationUnit(), namespace); if (grouping.getNamespaces().length > 2) { parentParent = grouping; } } return new MembersGrouping(parentParent, ns); } } // if we are doing grouping for the includes return the grouping container. if (element instanceof IInclude && fIncludesGrouping) { parent = new IncludesGrouping(((IInclude)element).getTranslationUnit()); } if (element instanceof IMacro && fMacroGrouping) { parent = new MacrosGrouping(((IMacro)element).getTranslationUnit()); } return parent; } protected Object[] getCProjects(ICModel cModel) throws CModelException { Object[] objects = cModel.getCProjects(); try { Object[] nonC = cModel.getNonCResources(); if (nonC.length > 0) { objects = concatenate(objects, nonC); } } catch (CModelException e) { // } return objects; } protected Object[] getSourceRoots(ICProject cproject) throws CModelException { if (!cproject.getProject().isOpen()) return NO_CHILDREN; List<ICElement> list= new ArrayList<ICElement>(); ICElement[] children = cproject.getChildren(); for (ICElement child : children) { if (child instanceof ISourceRoot && child.getResource().getType() == IResource.PROJECT) { // Was a source root at the project, get the children of this element ICElement[] c2 = ((ISourceRoot)child).getChildren(); for (int k = 0; k < c2.length; ++k) list.add(c2[k]); } else if (CCorePlugin.showSourceRootsAtTopOfProject()) { list.add(child); } else if (child instanceof ISourceRoot && child.getResource().getParent().equals(cproject.getProject())) { list.add(child); } } Object[] objects = list.toArray(); Object[] nonC = cproject.getNonCResources(); if (nonC != null && nonC.length > 0) { nonC = filterNonCResources(nonC, cproject); objects = concatenate(objects, nonC); } return objects; } protected Object[] getTranslationUnitChildren(ITranslationUnit unit) throws CModelException { Object[] children = unit.getChildren(); if (fIncludesGrouping) { boolean hasInclude = false; ArrayList<Object> list = new ArrayList<Object>(children.length); for (int i = 0; i < children.length; i++) { if (!(children[i] instanceof IInclude)) { list.add(children[i]); } else { hasInclude = true; } } if (hasInclude) { list.add (0, new IncludesGrouping(unit)); } children = list.toArray(); } Map<String, NamespacesGrouping> nsmap = new HashMap<String, NamespacesGrouping>(); if (fNamespacesGrouping) { // check if there is another namespace with the same name for the same parent List<Object> list = new ArrayList<Object>(children.length); for (int i = 0; i < children.length; ++i) { if (children[i] instanceof INamespace) { INamespace n1 = (INamespace)children[i]; NamespacesGrouping namespacesGrouping = nsmap.get(n1.getElementName()); if (namespacesGrouping == null) { namespacesGrouping = new NamespacesGrouping(unit, n1, fMemberGrouping); if (namespacesGrouping.getNamespaces().length > 1) { nsmap.put(n1.getElementName(), namespacesGrouping); list.add(namespacesGrouping); } else { list.add(children[i]); } } } else { list.add(children[i]); } } children = list.toArray(); } if (fMemberGrouping) { // check if there is another member with the same namespace for the same parent List<Object> list = new ArrayList<Object>(children.length); Map<String, MembersGrouping> map = new HashMap<String, MembersGrouping>(); for (int i = 0; i < children.length; ++i) { if (children[i] instanceof IMember) { final ICElement member = (ICElement)children[i]; String namespace = getElementNamespace(member); MembersGrouping memberGrouping = map.get(namespace); if (memberGrouping == null) { memberGrouping = new MembersGrouping(unit, namespace); map.put(namespace, memberGrouping); list.add(memberGrouping); } } else if (fNamespacesGrouping && children[i] instanceof INamespace) { if (!nsmap.containsKey(((INamespace) children[i]).getElementName())) { list.add(children[i]); } } else { list.add(children[i]); } } children = list.toArray(); } if (fMacroGrouping) { ArrayList<Object> list = new ArrayList<Object>(children.length); boolean hasMacros = false; for (int i = 0; i < children.length; i++) { if (!(children[i] instanceof IMacro)) list.add(children[i]); else hasMacros = true; } if (hasMacros) { //Check if include gouping is there. If so, put macros after if(!list.isEmpty()){ if(list.get(0) instanceof IncludesGrouping) list.add (1, new MacrosGrouping(unit)); else list.add (0, new MacrosGrouping(unit)); } else list.add (0, new MacrosGrouping(unit)); } children = list.toArray(); } return children; } protected Object[] getNamespaceChildren(IParent element) throws CModelException { Object[] children = element.getChildren(); if (fMemberGrouping) { // check if there is another member with the same namespace for the same parent List<Object> list = new ArrayList<Object>(children.length); Map<String, MembersGrouping> map = new HashMap<String, MembersGrouping>(); for (int i = 0; i < children.length; ++i) { if (children[i] instanceof IMember) { final ICElement member = (ICElement)children[i]; String namespace = getElementNamespace(member); MembersGrouping memberGrouping = map.get(namespace); if (memberGrouping == null) { memberGrouping = new MembersGrouping(element, namespace); map.put(namespace, memberGrouping); list.add(memberGrouping); } } else { list.add(children[i]); } } children = list.toArray(); } return children; } private static String getElementNamespace(ICElement member) { String name = member.getElementName(); int idx = name.lastIndexOf("::"); //$NON-NLS-1$ if (idx < 0) { return null; } return name.substring(0, idx); } protected Object[] getCResources(ICContainer container) throws CModelException { Object[] objects = null; ICElement[] children = container.getChildren(); List<ICElement> missingElements = Collections.emptyList(); if (!CCorePlugin.showSourceRootsAtTopOfProject()) { missingElements = getMissingElements(container, children); } try { objects = container.getNonCResources(); if (objects.length > 0) { objects = filterNonCResources(objects, container.getCProject()); } } catch (CModelException e) { } Object[] result = children; if (missingElements.size() > 0) { result = concatenate(result, missingElements.toArray()); } if (objects != null && objects.length > 0) { result = concatenate(result, objects); } return result; } private List<ICElement> getMissingElements(ICContainer container, ICElement[] elements) { // nested source roots may be filtered out below the project root, // we need to find them to add them back in List<ICElement> missingElements = new ArrayList<ICElement>(); try { List<IResource> missingContainers = new ArrayList<IResource>(); IResource[] allChildren = ((IContainer) container.getResource()).members(); for (IResource child : allChildren) { if (!(child instanceof IContainer)) continue; boolean found = false; for (ICElement element : elements) { if (element.getResource().equals(child)) { found = true; break; } } if (!found) missingContainers.add(child); } for (IResource resource : missingContainers) { ICElement element = container.getCProject().findElement(resource.getFullPath()); if (element != null) missingElements.add(element); } } catch (CoreException e1) { } return missingElements; } protected Object[] getResources(IProject project) { try { return project.members(); } catch (CoreException e) { } return NO_CHILDREN; } protected Object[] getResources(IFolder folder) throws CModelException { ICProject cproject = CoreModel.getDefault().create(folder.getProject()); Object[] members = null; try { members = folder.members(); } catch (CoreException e) { // } if (members == null || members.length == 0) { return NO_CHILDREN; } return filterNonCResources(members, cproject); } private Object[] filterNonCResources(Object[] objects, ICProject cproject) throws CModelException { ICElement[] binaries = null; ICElement[] archives = null; try { binaries = getBinaries(cproject); archives = getArchives(cproject); } catch (CModelException e) { archives = binaries = new ICElement[0]; } ISourceRoot[] roots = null; try { roots = cproject.getSourceRoots(); } catch (CModelException e) { roots = new ISourceRoot[0]; } List<Object> nonCResources = new ArrayList<Object>(objects.length); for (Object object : objects) { Object o= object; // A folder can also be a source root in the following case // Project // + src <- source folder // + excluded <- excluded from class path // + included <- a new source folder. // Included is a member of excluded, but since it is rendered as a source // folder we have to exclude it as a normal child. if (o instanceof IFolder) { IFolder folder = (IFolder)o; ISourceRoot root = null; for (int j = 0; j < roots.length; j++) { if (roots[j].getPath().equals(folder.getFullPath())) { root = roots[j]; break; } } // it is a sourceRoot skip it. if (root != null) { if (CCorePlugin.showSourceRootsAtTopOfProject()) continue; else o = root; } } else if (o instanceof IFile){ boolean found = false; for (ICElement binarie : binaries) { IResource res = binarie.getResource(); if (o.equals(res)) { o = binarie; found = true; break; } } if (!found) { for (ICElement archive : archives) { IResource res = archive.getResource(); if (o.equals(res)) { o = archive; break; } } } } nonCResources.add(o); } return nonCResources.toArray(); } /** * Note: This method is for internal use only. Clients should not call this method. */ protected boolean isProjectSourceRoot(ISourceRoot root) { IResource resource= root.getResource(); return (resource instanceof IProject); } protected boolean exists(Object element) { if (element == null) { return false; } if (element instanceof IResource) { return ((IResource)element).exists(); } if (element instanceof ICElement) { return ((ICElement)element).exists(); } return true; } protected IBinary[] getBinaries(ICProject cproject) throws CModelException { IBinaryContainer container = cproject.getBinaryContainer(); return getBinaries(container); } protected IBinary[] getBinaries(IBinaryContainer container) throws CModelException { ICElement[] celements = container.getChildren(); ArrayList<IBinary> list = new ArrayList<IBinary>(celements.length); for (ICElement celement : celements) { if (celement instanceof IBinary) { IBinary bin = (IBinary)celement; list.add(bin); } } IBinary[] bins = new IBinary[list.size()]; list.toArray(bins); return bins; } protected IArchive[] getArchives(ICProject cproject) throws CModelException { IArchiveContainer container = cproject.getArchiveContainer(); return getArchives(container); } protected IArchive[] getArchives(IArchiveContainer container) throws CModelException { ICElement[] celements = container.getChildren(); ArrayList<IArchive> list = new ArrayList<IArchive>(celements.length); for (ICElement celement : celements) { if (celement instanceof IArchive) { IArchive ar = (IArchive)celement; list.add(ar); } } IArchive[] ars = new IArchive[list.size()]; list.toArray(ars); return ars; } /** * Note: This method is for internal use only. Clients should not call this method. */ protected static Object[] concatenate(Object[] a1, Object[] a2) { int a1Len = a1.length; int a2Len = a2.length; Object[] res = new Object[a1Len + a2Len]; System.arraycopy(a1, 0, res, 0, a1Len); System.arraycopy(a2, 0, res, a1Len, a2Len); return res; } }