blob: 724a66de7083c2cb3f5537a744aef4bef3c1c7c8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.ui;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
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.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.ViewerComparator;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.eclipse.wst.jsdt.core.Flags;
import org.eclipse.wst.jsdt.core.IField;
import org.eclipse.wst.jsdt.core.IFunction;
import org.eclipse.wst.jsdt.core.IInitializer;
import org.eclipse.wst.jsdt.core.IJarEntryResource;
import org.eclipse.wst.jsdt.core.IJavaScriptElement;
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
import org.eclipse.wst.jsdt.core.IMember;
import org.eclipse.wst.jsdt.core.IPackageFragment;
import org.eclipse.wst.jsdt.core.IPackageFragmentRoot;
import org.eclipse.wst.jsdt.core.IType;
import org.eclipse.wst.jsdt.core.JavaScriptModelException;
import org.eclipse.wst.jsdt.core.Signature;
import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
import org.eclipse.wst.jsdt.internal.corext.util.JdtFlags;
import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
import org.eclipse.wst.jsdt.internal.ui.packageview.NamespaceGroup;
import org.eclipse.wst.jsdt.internal.ui.packageview.PackageFragmentRootContainer;
import org.eclipse.wst.jsdt.internal.ui.preferences.MembersOrderPreferenceCache;
/**
* Viewer comparator for JavaScript elements. Ordered by element category, then by element name.
* Package fragment roots are sorted as ordered on the classpath.
*
* <p>
* This class may be instantiated; it is not intended to be subclassed.
* </p>
*
* Provisional API: This class/interface is part of an interim API that is still under development and expected to
* change significantly before reaching stability. It is being made available at this early stage to solicit feedback
* from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
* (repeatedly) as the API evolves.
*/
public class JavaScriptElementComparator extends ViewerComparator {
private static final int PROJECTS= 1;
private static final int PACKAGEFRAGMENTROOTS= 2;
private static final int PACKAGEFRAGMENT= 3;
private static final int JAVASCRIPTUNITS= 4;
private static final int CLASSFILES= 5;
private static final int RESOURCEFOLDERS= 7;
private static final int RESOURCES= 8;
private static final int IMPORT_CONTAINER= 11;
private static final int IMPORT_DECLARATION= 12;
private static final int EXPORT_CONTAINER = 13;
private static final int EXPORT_DECLARATION = 14;
// Includes all categories ordered using the OutlineSortOrderPage:
// types, initializers, methods & fields
private static final int MEMBERSOFFSET= 15;
private static final int JAVAELEMENTS= 50;
private static final int OTHERS= 51;
private MembersOrderPreferenceCache fMemberOrderCache;
/**
* Constructor.
*/
public JavaScriptElementComparator() {
super(null); // delay initialization of collator
fMemberOrderCache= JavaScriptPlugin.getDefault().getMemberOrderPreferenceCache();
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ViewerComparator#category(java.lang.Object)
*/
public int category(Object element) {
if (element instanceof IJavaScriptElement) {
try {
IJavaScriptElement je= (IJavaScriptElement) element;
switch (je.getElementType()) {
case IJavaScriptElement.METHOD:
{
IFunction method= (IFunction) je;
if (method.isConstructor()) {
return getMemberCategory(MembersOrderPreferenceCache.CONSTRUCTORS_INDEX);
}
int flags= method.getFlags();
if (Flags.isStatic(flags))
return getMemberCategory(MembersOrderPreferenceCache.STATIC_METHODS_INDEX);
else
return getMemberCategory(MembersOrderPreferenceCache.METHOD_INDEX);
}
case IJavaScriptElement.FIELD :
{
int flags= ((IField) je).getFlags();
if (Flags.isStatic(flags))
return getMemberCategory(MembersOrderPreferenceCache.STATIC_FIELDS_INDEX);
else
return getMemberCategory(MembersOrderPreferenceCache.FIELDS_INDEX);
}
case IJavaScriptElement.INITIALIZER :
{
int flags= ((IInitializer) je).getFlags();
if (Flags.isStatic(flags))
return getMemberCategory(MembersOrderPreferenceCache.STATIC_INIT_INDEX);
else
return getMemberCategory(MembersOrderPreferenceCache.INIT_INDEX);
}
case IJavaScriptElement.TYPE :
return getMemberCategory(MembersOrderPreferenceCache.TYPE_INDEX);
case IJavaScriptElement.IMPORT_CONTAINER :
return IMPORT_CONTAINER;
case IJavaScriptElement.IMPORT_DECLARATION :
return IMPORT_DECLARATION;
case IJavaScriptElement.EXPORT_CONTAINER :
return EXPORT_CONTAINER;
case IJavaScriptElement.EXPORT_DECLARATION :
return EXPORT_DECLARATION;
case IJavaScriptElement.PACKAGE_FRAGMENT :
return PACKAGEFRAGMENT;
case IJavaScriptElement.PACKAGE_FRAGMENT_ROOT :
return PACKAGEFRAGMENTROOTS;
case IJavaScriptElement.JAVASCRIPT_PROJECT :
return PROJECTS;
case IJavaScriptElement.CLASS_FILE :
return CLASSFILES;
case IJavaScriptElement.JAVASCRIPT_UNIT :
// return JAVASCRIPTUNITS;
return RESOURCES;
}
} catch (JavaScriptModelException e) {
if (!e.isDoesNotExist())
JavaScriptPlugin.log(e);
}
return JAVAELEMENTS;
} else if (element instanceof IFile) {
return RESOURCES;
} else if (element instanceof IProject) {
return PROJECTS;
} else if (element instanceof IContainer) {
return RESOURCEFOLDERS;
} else if (element instanceof IJarEntryResource) {
if (((IJarEntryResource) element).isFile()) {
return RESOURCES;
}
return RESOURCEFOLDERS;
} else if (element instanceof PackageFragmentRootContainer) {
return PACKAGEFRAGMENTROOTS;
} else if (element instanceof ProjectLibraryRoot) {
return PROJECTS;
} else if (element instanceof NamespaceGroup) {
return PROJECTS;
}
return OTHERS;
}
private int getMemberCategory(int kind) {
int offset= fMemberOrderCache.getCategoryIndex(kind);
return offset + MEMBERSOFFSET;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
*/
public int compare(Viewer viewer, Object e1, Object e2) {
int cat1= category(e1);
int cat2= category(e2);
if (needsClasspathComparision(e1, cat1, e2, cat2)) {
IPackageFragmentRoot root1= getPackageFragmentRoot(e1);
IPackageFragmentRoot root2= getPackageFragmentRoot(e2);
if (root1 == null) {
if (root2 == null) {
return 0;
} else {
return 1;
}
} else if (root2 == null) {
return -1;
}
// check if not same to avoid expensive class path access
if (!root1.getPath().equals(root2.getPath())) {
int p1= getClassPathIndex(root1);
int p2= getClassPathIndex(root2);
if (p1 != p2) {
return p1 - p2;
}
}
}
if (cat1 != cat2)
return cat1 - cat2;
if (cat1 == PROJECTS || cat1 == RESOURCES || cat1 == RESOURCEFOLDERS || cat1 == OTHERS) {
String name1= getNonJavaElementLabel(viewer, e1);
String name2= getNonJavaElementLabel(viewer, e2);
if (name1 != null && name2 != null) {
return getComparator().compare(name1, name2);
}
return 0; // can't compare
}
// only JavaScript elements from this point
if (e1 instanceof IMember && e2 instanceof IMember) {
if (fMemberOrderCache.isSortByVisibility()) {
try {
int flags1= JdtFlags.getVisibilityCode((IMember) e1);
int flags2= JdtFlags.getVisibilityCode((IMember) e2);
int vis= fMemberOrderCache.getVisibilityIndex(flags1) - fMemberOrderCache.getVisibilityIndex(flags2);
if (vis != 0) {
return vis;
}
} catch (JavaScriptModelException ignore) {
}
}
}
String name1= getElementName(e1);
String name2= getElementName(e2);
if (e1 instanceof IType && e2 instanceof IType) { // handle anonymous types
if (name1.length() == 0) {
if (name2.length() == 0) {
try {
return getComparator().compare(((IType) e1).getSuperclassName(), ((IType) e2).getSuperclassName());
} catch (JavaScriptModelException e) {
return 0;
}
} else {
return 1;
}
} else if (name2.length() == 0) {
return -1;
}
}
int cmp= getComparator().compare(name1, name2);
if (cmp != 0) {
return cmp;
}
if (e1 instanceof IFunction && e2 instanceof IFunction) {
String[] params1= ((IFunction) e1).getParameterTypes();
String[] params2= ((IFunction) e2).getParameterTypes();
int len= Math.min(params1.length, params2.length);
for (int i = 0; i < len; i++) {
cmp= getComparator().compare(Signature.toString(params1[i]), Signature.toString(params2[i]));
if (cmp != 0) {
return cmp;
}
}
return params1.length - params2.length;
}
return 0;
}
private IPackageFragmentRoot getPackageFragmentRoot(Object element) {
if (element instanceof PackageFragmentRootContainer) {
// return first package fragment root from the container
PackageFragmentRootContainer cp= (PackageFragmentRootContainer)element;
Object[] roots= cp.getPackageFragmentRoots();
if (roots.length > 0)
return (IPackageFragmentRoot)roots[0];
// non resolvable - return null
return null;
}
return JavaModelUtil.getPackageFragmentRoot((IJavaScriptElement)element);
}
private String getNonJavaElementLabel(Viewer viewer, Object element) {
// try to use the workbench adapter for non - JavaScript resources or if not available, use the viewers label provider
if (element instanceof IResource) {
return ((IResource) element).getName();
}
if (element instanceof IStorage) {
return ((IStorage) element).getName();
}
if (element instanceof IAdaptable) {
IWorkbenchAdapter adapter= (IWorkbenchAdapter) ((IAdaptable) element).getAdapter(IWorkbenchAdapter.class);
if (adapter != null) {
return adapter.getLabel(element);
}
}
if (viewer instanceof ContentViewer) {
IBaseLabelProvider prov = ((ContentViewer) viewer).getLabelProvider();
if (prov instanceof ILabelProvider) {
return ((ILabelProvider) prov).getText(element);
}
}
return null;
}
private int getClassPathIndex(IPackageFragmentRoot root) {
try {
IPath rootPath= root.getPath();
IPackageFragmentRoot[] roots= root.getJavaScriptProject().getPackageFragmentRoots();
for (int i= 0; i < roots.length; i++) {
if (roots[i].getPath().equals(rootPath)) {
return i;
}
}
} catch (JavaScriptModelException e) {
}
return Integer.MAX_VALUE;
}
private boolean needsClasspathComparision(Object e1, int cat1, Object e2, int cat2) {
if ((cat1 == PACKAGEFRAGMENTROOTS && cat2 == PACKAGEFRAGMENTROOTS) ||
(cat1 == PACKAGEFRAGMENT &&
((IPackageFragment)e1).getParent().getResource() instanceof IProject &&
cat2 == PACKAGEFRAGMENTROOTS) ||
(cat1 == PACKAGEFRAGMENTROOTS &&
cat2 == PACKAGEFRAGMENT &&
((IPackageFragment)e2).getParent().getResource() instanceof IProject)) {
IJavaScriptProject p1= getJavaProject(e1);
return p1 != null && p1.equals(getJavaProject(e2));
}
return false;
}
private IJavaScriptProject getJavaProject(Object element) {
if (element instanceof IJavaScriptElement) {
return ((IJavaScriptElement)element).getJavaScriptProject();
} else if (element instanceof PackageFragmentRootContainer) {
return ((PackageFragmentRootContainer)element).getJavaProject();
}
return null;
}
private String getElementName(Object element) {
if (element instanceof IJavaScriptElement) {
return ((IJavaScriptElement)element).getElementName();
} else if (element instanceof PackageFragmentRootContainer) {
return ((PackageFragmentRootContainer)element).getLabel();
} else {
return element.toString();
}
}
}