blob: 4657b71b464fdd36977535826768e48384cfda42 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 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
*
*******************************************************************************/
package org.eclipse.dltk.ui;
import java.util.Comparator;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IDLTKLanguageToolkit;
import org.eclipse.dltk.core.IMethod;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IProjectFragment;
import org.eclipse.dltk.core.IScriptFolder;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.IType;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.ScriptModelUtil;
import org.eclipse.dltk.internal.ui.UIModelProviderManager;
import org.eclipse.dltk.internal.ui.scriptview.BuildPathContainer;
import org.eclipse.dltk.ui.IModelCompareProvider.CompareResult;
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;
import com.ibm.icu.text.Collator;
/**
* Sorter for Script elements. Ordered by element category, then by element
* name. Package fragment roots are sorted as ordered on the buildpath.
*
*/
public class ModelElementSorter extends ViewerSorter
implements IModelCompareCategories {
private MembersOrderPreferenceCache fMemberOrderCache;
private Collator fNewCollator; // collator from ICU
private boolean innerElements = true;
// Some category definition interfaces
/**
* Constructor.
*/
public ModelElementSorter() {
super(null); // delay initialization of collator
fMemberOrderCache = DLTKUIPlugin.getDefault()
.getMemberOrderPreferenceCache();
fNewCollator = null;
}
public boolean isInnerElements() {
return innerElements;
}
public void setInnerElements(boolean innerElements) {
this.innerElements = innerElements;
}
@Override
public int category(Object element) {
IModelCompareProvider[] providers = getCompareProviders(element);
for (int i = 0; i < providers.length; i++) {
Integer category = providers[i].category(element);
if (category != null) {
return category.intValue();
}
}
if (element instanceof IModelElement) {
try {
IModelElement je = (IModelElement) element;
switch (je.getElementType()) {
case IModelElement.METHOD: {
IMethod method = (IMethod) je;
if (method.isConstructor()) {
return getMemberCategory(
MembersOrderPreferenceCache.CONSTRUCTORS_INDEX);
}
return getMemberCategory(
MembersOrderPreferenceCache.METHOD_INDEX);
}
case IModelElement.FIELD: {
return getMemberCategory(
MembersOrderPreferenceCache.FIELDS_INDEX);
}
// case IModelElement.INITIALIZER :
// {
// int flags= ((IInitializer) je).getFlags();
// if (Flags.isStatic(flags))
// return
// getMemberCategory(MembersOrderPreferenceCache.STATIC_INIT_INDEX);
// else
// return
// getMemberCategory(MembersOrderPreferenceCache.INIT_INDEX);
// }
case IModelElement.TYPE:
return getMemberCategory(
MembersOrderPreferenceCache.TYPE_INDEX);
case IModelElement.PACKAGE_DECLARATION:
return PACKAGE_DECL;
case IModelElement.IMPORT_CONTAINER:
return IMPORT_CONTAINER;
case IModelElement.IMPORT_DECLARATION:
return IMPORT_DECLARATION;
case IModelElement.SCRIPT_FOLDER:
return SCRIPTFOLDER;
case IModelElement.PROJECT_FRAGMENT:
return PROJECTFRAGMENT;
case IModelElement.SCRIPT_PROJECT:
return PROJECTS;
case IModelElement.SOURCE_MODULE:
return SOURCEMODULES;
}
} catch (ModelException e) {
if (!e.isDoesNotExist())
DLTKUIPlugin.log(e);
}
return SCRIPT_ELEMENTS;
} 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 IStorage) {
return STORAGE;
} else if (element instanceof BuildPathContainer) {
return CONTAINER;
}
return OTHERS;
}
private IModelCompareProvider[] getCompareProviders(Object element) {
String toolkit = null;
if (element instanceof IModelElement) {
IDLTKLanguageToolkit tk = DLTKLanguageManager
.getLanguageToolkit((IModelElement) element);
if (tk != null) {
toolkit = tk.getNatureId();
}
}
IModelCompareProvider[] providers = UIModelProviderManager
.getCompareProviders(toolkit);
return providers;
}
private int getMemberCategory(int kind) {
int offset = fMemberOrderCache.getCategoryIndex(kind);
return offset + MEMBERSOFFSET;
}
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
int cat1 = category(e1);
int cat2 = category(e2);
IModelCompareProvider[] providers = getCompareProviders(e1);
for (int i = 0; i < providers.length; i++) {
CompareResult category = providers[i].compare(e1, e2, cat1, cat2);
if (category != null) {
return category.result;
}
}
if (needsBuildpathComparision(e1, cat1, e2, cat2)) {
IProjectFragment root1 = getProjectFragment(e1);
IProjectFragment root2 = getProjectFragment(e2);
if (root1 == null) {
if (root2 == null) {
return 0;
}
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 = getBuildpathIndex(root1);
int p2 = getBuildpathIndex(root2);
if (p1 != p2) {
return p1 - p2;
}
}
}
if (cat1 != cat2)
return cat1 - cat2;
if (cat1 == PROJECTS || cat1 == RESOURCES || cat1 == RESOURCEFOLDERS
|| cat1 == STORAGE || cat1 == OTHERS) {
String name1 = getNonScriptElementLabel(viewer, e1);
String name2 = getNonScriptElementLabel(viewer, e2);
if (name1 != null && name2 != null) {
return getNewCollator().compare(name1, name2);
}
return 0; // can't compare
}
// only script elements from this point
String name1 = getElementName(e1);
String name2 = getElementName(e2);
// If
if (!this.isInnerElements() && e1 instanceof IModelElement) {
IModelElement me = (IModelElement) e1;
if (me.getElementType() > IModelElement.BINARY_MODULE) {
return 0;
}
}
if (e1 instanceof IType && e2 instanceof IType) { // handle anonymous
// types
if (name1.length() == 0) {
if (name2.length() == 0) {
try {
String sc1[] = ((IType) e1).getSuperClasses();
String sc2[] = ((IType) e2).getSuperClasses();
if (sc1 != null && sc2 != null && sc1.length > 0
&& sc2.length > 0) {
return getNewCollator().compare(sc1[0], sc2[0]);
}
return 0;
} catch (ModelException e) {
return 0;
}
}
return 1;
} else if (name2.length() == 0) {
return -1;
}
}
int cmp = getNewCollator().compare(name1, name2);
if (cmp != 0) {
return cmp;
}
if (e1 instanceof IMethod && e2 instanceof IMethod) {
String[] params1 = null;
try {
params1 = ((IMethod) e1).getParameterNames();
} catch (ModelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String[] params2 = null;
try {
params2 = ((IMethod) e2).getParameterNames();
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
if (params1 != null && params2 != null) {
int len = Math.min(params1.length, params2.length);
for (int i = 0; i < len; i++) {
cmp = getNewCollator().compare(params1[i], params2[i]);
if (cmp != 0) {
return cmp;
}
}
}
return params1.length - params2.length;
}
return 0;
}
private IProjectFragment getProjectFragment(Object element) {
if (element instanceof BuildPathContainer) {
// return first package fragment root from the container
BuildPathContainer cp = (BuildPathContainer) element;
Object[] roots = cp.getProjectFragments();
if (roots.length > 0)
return (IProjectFragment) roots[0];
// non resolvable - return null
return null;
}
return ScriptModelUtil.getProjectFragment((IModelElement) element);
}
private String getNonScriptElementLabel(Viewer viewer, Object element) {
// try to use the workbench adapter for non - script resources or if not
// available, use the viewers label provider
if (element instanceof IAdaptable) {
IWorkbenchAdapter adapter = ((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 getBuildpathIndex(IProjectFragment root) {
try {
IPath rootPath = root.getPath();
IProjectFragment[] roots = root.getScriptProject()
.getProjectFragments();
for (int i = 0; i < roots.length; i++) {
if (roots[i].getPath().equals(rootPath)) {
return i;
}
}
} catch (ModelException e) {
}
return Integer.MAX_VALUE;
}
private final Comparator getNewCollator() {
if (fNewCollator == null) {
fNewCollator = Collator.getInstance();
}
return fNewCollator;
}
private boolean needsBuildpathComparision(Object e1, int cat1, Object e2,
int cat2) {
if ((cat1 == PROJECTFRAGMENT && cat2 == PROJECTFRAGMENT)
|| (cat1 == CONTAINER && cat2 == CONTAINER)
|| (cat1 == SCRIPTFOLDER
&& ((IScriptFolder) e1).getParent()
.getResource() instanceof IProject
&& cat2 == PROJECTFRAGMENT)
|| (cat1 == PROJECTFRAGMENT && cat2 == SCRIPTFOLDER
&& ((IScriptFolder) e2).getParent()
.getResource() instanceof IProject)) {
IScriptProject p1 = getScriptProject(e1);
return p1 != null && p1.equals(getScriptProject(e2));
}
return false;
}
private IScriptProject getScriptProject(Object element) {
if (element instanceof IModelElement) {
return ((IModelElement) element).getScriptProject();
} else if (element instanceof BuildPathContainer) {
return ((BuildPathContainer) element).getScriptProject();
}
return null;
}
protected String getElementName(Object element) {
if (element instanceof IModelElement) {
return ((IModelElement) element).getElementName();
} else if (element instanceof BuildPathContainer) {
return ((BuildPathContainer) element).getLabel();
} else if (element instanceof IFile) {
// handle SOURCEMODULES category returned for IFile
return ((IFile) element).getName();
} else {
return element.toString();
}
}
}