| /******************************************************************************* |
| * Copyright (c) 2005, 2015 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * QNX Software System |
| * Anton Leherbauer (Wind River Systems) |
| * Markus Schorn (Wind River Systems) |
| *******************************************************************************/ |
| package org.eclipse.cdt.ui; |
| |
| import java.util.Comparator; |
| |
| import org.eclipse.cdt.core.model.CModelException; |
| import org.eclipse.cdt.core.model.CoreModel; |
| import org.eclipse.cdt.core.model.IArchiveContainer; |
| import org.eclipse.cdt.core.model.IBinaryContainer; |
| import org.eclipse.cdt.core.model.ICElement; |
| import org.eclipse.cdt.core.model.IIncludeReference; |
| import org.eclipse.cdt.core.model.ILibraryReference; |
| import org.eclipse.cdt.core.model.IMember; |
| import org.eclipse.cdt.core.model.IMethodDeclaration; |
| import org.eclipse.cdt.core.model.ISourceRoot; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IStorage; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.viewers.ContentViewer; |
| import org.eclipse.jface.viewers.IBaseLabelProvider; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerSorter; |
| import org.eclipse.ui.model.IWorkbenchAdapter; |
| |
| /** |
| * A sorter to sort the file and the folders in the C viewer in the following order: |
| * 1 Project |
| * 2 BinaryContainer |
| * 3 ArchiveContainer |
| * 4 LibraryContainer |
| * 5 IncludeContainer |
| * 6 Source roots |
| * 5 C Elements |
| * 6 non C Elements |
| * |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public class CElementSorter extends ViewerSorter { |
| |
| protected static final int CMODEL = 0; |
| protected static final int PROJECTS = 10; |
| protected static final int BINARYCONTAINER = 12; |
| protected static final int ARCHIVECONTAINER = 13; |
| protected static final int INCLUDEREFCONTAINER = 14; |
| protected static final int LIBRARYREFCONTAINER = 15; |
| protected static final int SOURCEROOTS = 16; |
| protected static final int CCONTAINERS = 17; |
| protected static final int LIBRARYREFERENCES = 18; |
| protected static final int INCLUDEREFERENCES = 19; |
| protected static final int TRANSLATIONUNIT_HEADERS = 20; |
| protected static final int TRANSLATIONUNIT_SOURCE = 21; |
| protected static final int TRANSLATIONUNITS = 22; |
| protected static final int BINARIES = 23; |
| protected static final int ARCHIVES = 24; |
| |
| protected static final int INCLUDES = 28; |
| protected static final int MACROS = 29; |
| protected static final int USINGS = 30; |
| protected static final int NAMESPACES = 32; |
| protected static final int NAMESPACES_RESERVED = 33; |
| protected static final int NAMESPACES_SYSTEM = 34; |
| /** |
| * @since 5.1 |
| */ |
| protected static final int TYPES = 35; |
| |
| @Deprecated |
| protected static final int VARIABLEDECLARATIONS = 36; |
| @Deprecated |
| protected static final int FUNCTIONDECLARATIONS = 37; |
| protected static final int VARIABLES = 38; |
| protected static final int VARIABLES_RESERVED = 39; |
| protected static final int VARIABLES_SYSTEM = 40; |
| protected static final int FUNCTIONS = 41; |
| protected static final int FUNCTIONS_RESERVED = 42; |
| protected static final int FUNCTIONS_SYSTEM = 43; |
| @Deprecated |
| protected static final int METHODDECLARATIONS = 44; |
| |
| protected static final int CELEMENTS = 100; |
| protected static final int CELEMENTS_RESERVED = 101; |
| protected static final int CELEMENTS_SYSTEM = 102; |
| |
| protected static final int RESOURCEFOLDERS = 200; |
| protected static final int RESOURCES = 201; |
| protected static final int STORAGE = 202; |
| protected static final int OTHERS = 500; |
| |
| /* |
| * Constants added for names starting with '_' or '__' |
| */ |
| private static final int NORMAL = 0; |
| private static final int RESERVED = 1; |
| private static final int SYSTEM = 2; |
| |
| /* |
| * Constants for ordering different member kinds. |
| */ |
| private static final int STATIC_MEMBER = 0; |
| private static final int CONSTRUCTOR = 1; |
| private static final int DESTRUCTOR = 2; |
| private static final int MEMBER = 3; |
| |
| /** |
| * Flag indicating whether header files and source files should be separated. |
| * If <code>true</code>, header files will be sorted before source files, |
| * otherwise header and source files will be sorted by name. |
| */ |
| private boolean fSeparateHeaderAndSource; |
| |
| private boolean fKeepSortOrderOfExcludedFiles; |
| |
| /** |
| * Default constructor for use as executable extension. |
| */ |
| public CElementSorter() { |
| final IPreferenceStore store = CUIPlugin.getDefault().getPreferenceStore(); |
| fSeparateHeaderAndSource = store.getBoolean(PreferenceConstants.CVIEW_SEPARATE_HEADER_AND_SOURCE); |
| fKeepSortOrderOfExcludedFiles = store.getBoolean(PreferenceConstants.SORT_ORDER_OF_EXCLUDED_FILES); |
| } |
| |
| @Override |
| public int category(Object element) { |
| if (element instanceof ICElement) { |
| ICElement cElement = (ICElement) element; |
| switch (cElement.getElementType()) { |
| case ICElement.C_MODEL: |
| return CMODEL; |
| case ICElement.C_PROJECT: |
| return PROJECTS; |
| case ICElement.C_CCONTAINER: |
| if (element instanceof ISourceRoot) { |
| return SOURCEROOTS; |
| } |
| return CCONTAINERS; |
| case ICElement.C_VCONTAINER: |
| if (element instanceof IBinaryContainer) { |
| return BINARYCONTAINER; |
| } else if (element instanceof IArchiveContainer) { |
| return ARCHIVECONTAINER; |
| } else if (element instanceof ILibraryReference) { |
| return LIBRARYREFERENCES; |
| } else if (element instanceof IIncludeReference) { |
| return INCLUDEREFERENCES; |
| } |
| return CCONTAINERS; |
| case ICElement.C_UNIT: |
| return getTranslationUnitCategory(cElement.getCProject().getProject(), cElement.getElementName()); |
| case ICElement.C_INCLUDE: |
| return INCLUDES; |
| case ICElement.C_MACRO: |
| return MACROS; |
| case ICElement.C_NAMESPACE: |
| return NAMESPACES + getNameKind(cElement.getElementName()); |
| case ICElement.C_USING: |
| return USINGS; |
| case ICElement.C_TYPEDEF: |
| case ICElement.C_CLASS: |
| case ICElement.C_CLASS_DECLARATION: |
| case ICElement.C_TEMPLATE_CLASS: |
| case ICElement.C_TEMPLATE_CLASS_DECLARATION: |
| case ICElement.C_STRUCT: |
| case ICElement.C_STRUCT_DECLARATION: |
| case ICElement.C_TEMPLATE_STRUCT: |
| case ICElement.C_TEMPLATE_STRUCT_DECLARATION: |
| case ICElement.C_UNION: |
| case ICElement.C_UNION_DECLARATION: |
| case ICElement.C_TEMPLATE_UNION: |
| case ICElement.C_TEMPLATE_UNION_DECLARATION: |
| case ICElement.C_ENUMERATION: |
| return TYPES; |
| case ICElement.C_VARIABLE_DECLARATION: |
| case ICElement.C_VARIABLE: |
| case ICElement.C_TEMPLATE_VARIABLE: |
| case ICElement.C_FIELD: |
| return VARIABLES + getNameKind(cElement.getElementName()); |
| case ICElement.C_FUNCTION_DECLARATION: |
| case ICElement.C_FUNCTION: |
| case ICElement.C_TEMPLATE_FUNCTION_DECLARATION: |
| case ICElement.C_TEMPLATE_FUNCTION: |
| case ICElement.C_METHOD_DECLARATION: |
| case ICElement.C_METHOD: |
| case ICElement.C_TEMPLATE_METHOD_DECLARATION: |
| case ICElement.C_TEMPLATE_METHOD: |
| return FUNCTIONS + getNameKind(cElement.getElementName()); |
| case ICElement.C_ARCHIVE: |
| return ARCHIVES; |
| case ICElement.C_BINARY: |
| return BINARIES; |
| default: |
| return CELEMENTS + getNameKind(cElement.getElementName()); |
| } |
| } else if (element instanceof IResource) { |
| IResource resource = (IResource) element; |
| switch (resource.getType()) { |
| case IResource.PROJECT: |
| return PROJECTS; |
| case IResource.FOLDER: |
| return RESOURCEFOLDERS; |
| default: // translation unit that was excluded from the build |
| if (fKeepSortOrderOfExcludedFiles |
| && CoreModel.isValidTranslationUnitName(resource.getProject(), resource.getName())) { |
| return getTranslationUnitCategory(resource.getProject(), resource.getName()); |
| } |
| return RESOURCES; |
| } |
| } else if (element instanceof IStorage) { |
| return STORAGE; |
| } else if (element instanceof CElementGrouping) { |
| int type = ((CElementGrouping) element).getType(); |
| switch (type) { |
| case CElementGrouping.INCLUDES_GROUPING: |
| return INCLUDES; |
| case CElementGrouping.MACROS_GROUPING: |
| return MACROS; |
| case CElementGrouping.CLASS_GROUPING: |
| return TYPES; |
| case CElementGrouping.NAMESPACE_GROUPING: |
| return NAMESPACES; |
| case CElementGrouping.LIBRARY_REF_CONTAINER: |
| return LIBRARYREFCONTAINER; |
| case CElementGrouping.INCLUDE_REF_CONTAINER: |
| return INCLUDEREFCONTAINER; |
| } |
| } |
| return OTHERS; |
| } |
| |
| private int getNameKind(String name) { |
| int length = name.length(); |
| if (length > 0 && name.charAt(0) == '_') { |
| if (length > 1 && name.charAt(1) == '_') { |
| return SYSTEM; |
| } |
| return RESERVED; |
| } |
| return NORMAL; |
| } |
| |
| private int getTranslationUnitCategory(IProject project, String name) { |
| if (fSeparateHeaderAndSource) { |
| if (CoreModel.isValidHeaderUnitName(project, name)) { |
| return TRANSLATIONUNIT_HEADERS; |
| } |
| if (CoreModel.isValidSourceUnitName(project, name)) { |
| return TRANSLATIONUNIT_SOURCE; |
| } |
| } |
| return TRANSLATIONUNITS; |
| } |
| |
| @Override |
| public int compare(Viewer viewer, Object e1, Object e2) { |
| int cat1 = category(e1); |
| int cat2 = category(e2); |
| |
| if (cat1 != cat2) |
| return cat1 - cat2; |
| |
| // cat1 == cat2 |
| |
| final Comparator<? super String> comparator = getComparator(); |
| if (cat1 == PROJECTS) { |
| IWorkbenchAdapter a1 = ((IAdaptable) e1).getAdapter(IWorkbenchAdapter.class); |
| IWorkbenchAdapter a2 = ((IAdaptable) e2).getAdapter(IWorkbenchAdapter.class); |
| return comparator.compare(a1.getLabel(e1), a2.getLabel(e2)); |
| } |
| |
| if (cat1 == SOURCEROOTS) { |
| ISourceRoot root1 = getSourceRoot(e1); |
| ISourceRoot root2 = getSourceRoot(e2); |
| if (root1 == null) { |
| if (root2 == null) { |
| return 0; |
| } |
| return 1; |
| } else if (root2 == null) { |
| return -1; |
| } |
| if (!root1.getPath().equals(root2.getPath())) { |
| int p1 = getPathEntryIndex(root1); |
| int p2 = getPathEntryIndex(root2); |
| if (p1 != p2) { |
| return p1 - p2; |
| } |
| } |
| } |
| |
| // non - c resources are sorted using the label from the viewers label provider |
| if (cat1 == RESOURCES || cat1 == RESOURCEFOLDERS || cat1 == STORAGE || cat1 == OTHERS) { |
| return compareWithLabelProvider(viewer, e1, e2); |
| } |
| |
| String ns1 = ""; //$NON-NLS-1$ |
| String ns2 = ns1; |
| |
| String name1; |
| String name2; |
| |
| if (e1 instanceof ICElement) { |
| name1 = ((ICElement) e1).getElementName(); |
| int idx = name1.lastIndexOf("::"); //$NON-NLS-1$ |
| if (idx >= 0) { |
| ns1 = name1.substring(0, idx); |
| name1 = name1.substring(idx + 2); |
| } |
| if (name1.length() > 0 && name1.charAt(0) == '~') { |
| name1 = name1.substring(1); |
| } |
| } else if (e1 instanceof IResource) { |
| name1 = ((IResource) e1).getName(); |
| } else { |
| name1 = e1.toString(); |
| } |
| if (e2 instanceof ICElement) { |
| name2 = ((ICElement) e2).getElementName(); |
| int idx = name2.lastIndexOf("::"); //$NON-NLS-1$ |
| if (idx >= 0) { |
| ns2 = name2.substring(0, idx); |
| name2 = name2.substring(idx + 2); |
| } |
| if (name2.length() > 0 && name2.charAt(0) == '~') { |
| name2 = name2.substring(1); |
| } |
| } else if (e2 instanceof IResource) { |
| name2 = ((IResource) e2).getName(); |
| } else { |
| name2 = e2.toString(); |
| } |
| |
| // compare namespace |
| int result = comparator.compare(ns1, ns2); |
| if (result != 0) { |
| return result; |
| } |
| |
| // compare method/member kind |
| if (e1 instanceof IMethodDeclaration && e2 instanceof IMethodDeclaration) { |
| result = getMethodKind((IMethodDeclaration) e1) - getMethodKind((IMethodDeclaration) e2); |
| } else if (e1 instanceof IMember && e2 instanceof IMember) { |
| result = getMemberKind((IMember) e1) - getMemberKind((IMember) e2); |
| } |
| if (result != 0) { |
| return result; |
| } |
| |
| // compare simple name |
| result = comparator.compare(name1, name2); |
| if (result != 0) { |
| return result; |
| } |
| return result; |
| } |
| |
| private int getMethodKind(IMethodDeclaration method) { |
| try { |
| if (method.isStatic()) { |
| return STATIC_MEMBER; |
| } |
| if (method.isConstructor()) { |
| return CONSTRUCTOR; |
| } |
| if (method.isDestructor()) { |
| return DESTRUCTOR; |
| } |
| } catch (CModelException exc) { |
| // ignore |
| } |
| return MEMBER; |
| } |
| |
| private int getMemberKind(IMember member) { |
| try { |
| if (member.isStatic()) { |
| return STATIC_MEMBER; |
| } |
| } catch (CModelException exc) { |
| // ignore |
| } |
| return MEMBER; |
| } |
| |
| private ISourceRoot getSourceRoot(Object element) { |
| ICElement celement = (ICElement) element; |
| while (!(celement instanceof ISourceRoot) && celement != null) { |
| celement = celement.getParent(); |
| } |
| return (ISourceRoot) celement; |
| } |
| |
| private int compareWithLabelProvider(Viewer viewer, Object e1, Object e2) { |
| if (viewer instanceof ContentViewer) { |
| IBaseLabelProvider prov = ((ContentViewer) viewer).getLabelProvider(); |
| if (prov instanceof ILabelProvider) { |
| ILabelProvider lprov = (ILabelProvider) prov; |
| String name1 = lprov.getText(e1); |
| String name2 = lprov.getText(e2); |
| if (name1 != null && name2 != null) { |
| final Comparator<? super String> comparator = getComparator(); |
| return comparator.compare(name1, name2); |
| } |
| } |
| } |
| return 0; // can't compare |
| } |
| |
| private int getPathEntryIndex(ISourceRoot root) { |
| try { |
| IPath rootPath = root.getPath(); |
| ISourceRoot[] roots = root.getCProject().getSourceRoots(); |
| for (int i = 0; i < roots.length; i++) { |
| if (roots[i].getPath().equals(rootPath)) { |
| return i; |
| } |
| } |
| } catch (CModelException e) { |
| } |
| |
| return Integer.MAX_VALUE; |
| } |
| |
| } |