/*
* Created on Mar 21, 2004
*
* To change the template for this generated file go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
package org.rubypeople.rdt.ui;
import java.text.Collator;
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.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 org.rubypeople.rdt.core.IMember;
import org.rubypeople.rdt.core.IMethod;
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.IRubyProject;
import org.rubypeople.rdt.core.ISourceFolder;
import org.rubypeople.rdt.core.ISourceFolderRoot;
import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.internal.corext.util.RubyModelUtil;
import org.rubypeople.rdt.internal.ui.RubyPlugin;
import org.rubypeople.rdt.internal.ui.packageview.LoadPathContainer;
import org.rubypeople.rdt.internal.ui.preferences.MembersOrderPreferenceCache;
/**
* @author Chris
*/
public class RubyElementSorter extends ViewerSorter {
private static final int PROJECTS = 1;
private static final int SOURCEFOLDERROOTS= 2;
private static final int SOURCEFOLDER= 3;
private static final int RUBYSCRIPTS = 4;
private static final int RESOURCEFOLDERS = 7;
private static final int RESOURCES = 8;
private static final int STORAGE = 9;
private static final int IMPORT_CONTAINER = 11;
private static final int IMPORT_DECLARATION = 12;
// Includes all categories ordered using the OutlineSortOrderPage:
// types, initializers, methods & fields
private static final int MEMBERSOFFSET = 15;
private static final int RUBYELEMENTS = 50;
private static final int OTHERS = 51;
private MembersOrderPreferenceCache fMemberOrderCache;
private Collator fNewCollator; // collator from ICU
/**
* Constructor.
*/
public RubyElementSorter() {
super(null); // delay initialization of collator
fMemberOrderCache= RubyPlugin.getDefault().getMemberOrderPreferenceCache();
fNewCollator= null;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ViewerSorter#getCollator()
*/
public final Collator getCollator() {
if (collator == null) {
collator= Collator.getInstance();
}
return collator;
}
/*
* @see ViewerSorter#category
*/
public int category(Object element) {
if (element instanceof IRubyElement) {
IRubyElement je = (IRubyElement) element;
switch (je.getElementType()) {
case IRubyElement.METHOD: {
IMethod method = (IMethod) je;
if (method.isConstructor()) { return getMemberCategory(MembersOrderPreferenceCache.CONSTRUCTORS_INDEX); }
if (method.isSingleton())
return getMemberCategory(MembersOrderPreferenceCache.STATIC_METHODS_INDEX);
else
return getMemberCategory(MembersOrderPreferenceCache.METHOD_INDEX);
}
case IRubyElement.FIELD:
case IRubyElement.CLASS_VAR:
case IRubyElement.INSTANCE_VAR:
case IRubyElement.CONSTANT: {
return getMemberCategory(MembersOrderPreferenceCache.FIELDS_INDEX);
}
case IRubyElement.TYPE:
return getMemberCategory(MembersOrderPreferenceCache.TYPE_INDEX);
case IRubyElement.IMPORT_CONTAINER:
return IMPORT_CONTAINER;
case IRubyElement.IMPORT_DECLARATION:
return IMPORT_DECLARATION;
case IRubyElement.SOURCE_FOLDER :
return SOURCEFOLDER;
case IRubyElement.SOURCE_FOLDER_ROOT :
return SOURCEFOLDERROOTS;
case IRubyElement.RUBY_PROJECT:
return PROJECTS;
case IRubyElement.SCRIPT:
return RUBYSCRIPTS;
}
return RUBYELEMENTS;
} 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 LoadPathContainer) {
return SOURCEFOLDERROOTS;
}
return OTHERS;
}
private int getMemberCategory(int kind) {
int offset = fMemberOrderCache.getCategoryIndex(kind);
return offset + MEMBERSOFFSET;
}
/*
* @see ViewerSorter#compare
*/
public int compare(Viewer viewer, Object e1, Object e2) {
int cat1 = category(e1);
int cat2 = category(e2);
if (needsLoadpathComparision(e1, cat1, e2, cat2)) {
ISourceFolderRoot root1= getSourceFolderRoot(e1);
ISourceFolderRoot root2= getSourceFolderRoot(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= getLoadPathIndex(root1);
int p2= getLoadPathIndex(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 = getNonRubyElementLabel(viewer, e1);
String name2 = getNonRubyElementLabel(viewer, e2);
if (name1 != null && name2 != null) { return getCollator().compare(name1, name2); }
return 0; // can't compare
}
// only ruby elements from this point
if (e1 instanceof IMethod) {
if (fMemberOrderCache.isSortByVisibility()) {
try {
int flags1 = ((IMethod) e1).getVisibility();
int flags2 = ((IMethod) e2).getVisibility();
int vis = fMemberOrderCache.getVisibilityIndex(flags1)
- fMemberOrderCache.getVisibilityIndex(flags2);
if (vis != 0) { return vis; }
} catch (RubyModelException ignore) {
}
}
}
if (e1 instanceof IMember) {
// FIXME Sort members differently! (Constants, Class vars, instance
// vars)
}
String name1 = getElementName(e1);
String name2 = getElementName(e2);
if (e1 instanceof IType) { // handle anonymous types
if (name1.length() == 0) {
if (name2.length() == 0) {
try {
return getCollator().compare(((IType) e1).getSuperclassName(),
((IType) e2).getSuperclassName());
} catch (RubyModelException e) {
return 0;
}
} else {
return 1;
}
} else if (name2.length() == 0) { return -1; }
}
int cmp = getCollator().compare(name1, name2);
if (cmp != 0) { return cmp; }
try {
if (e1 instanceof IMethod) {
String[] params1 = ((IMethod) e1).getParameterNames();
String[] params2 = ((IMethod) e2).getParameterNames();
int len = Math.min(params1.length, params2.length);
for (int i = 0; i < len; i++) {
cmp = getCollator().compare(params1[i], params2[i]);
if (cmp != 0) { return cmp; }
}
return params1.length - params2.length;
}
return 0;
} catch (RubyModelException e) {
return 0;
}
}
protected String getElementName(Object element) {
if (element instanceof IRubyElement) {
return ((IRubyElement) element).getElementName();
} else {
return element.toString();
}
}
private String getNonRubyElementLabel(Viewer viewer, Object element) {
// try to use the workbench adapter for non - ruby resources or if not
// available, use the viewers label provider
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 boolean needsLoadpathComparision(Object e1, int cat1, Object e2, int cat2) {
if ((cat1 == SOURCEFOLDERROOTS && cat2 == SOURCEFOLDERROOTS) ||
(cat1 == SOURCEFOLDER &&
((ISourceFolder)e1).getParent().getResource() instanceof IProject &&
cat2 == SOURCEFOLDERROOTS) ||
(cat1 == SOURCEFOLDERROOTS &&
cat2 == SOURCEFOLDER &&
((ISourceFolder)e2).getParent().getResource() instanceof IProject)) {
IRubyProject p1= getRubyProject(e1);
return p1 != null && p1.equals(getRubyProject(e2));
}
return false;
}
private IRubyProject getRubyProject(Object element) {
if (element instanceof IRubyElement) {
return ((IRubyElement)element).getRubyProject();
} else if (element instanceof LoadPathContainer) {
return ((LoadPathContainer)element).getRubyProject();
}
return null;
}
private ISourceFolderRoot getSourceFolderRoot(Object element) {
if (element instanceof LoadPathContainer) {
// return first source folder root from the container
LoadPathContainer cp= (LoadPathContainer)element;
Object[] roots= cp.getSourceFolderRoots();
if (roots.length > 0)
return (ISourceFolderRoot)roots[0];
// non resolvable - return null
return null;
}
return RubyModelUtil.getSourceFolderRoot((IRubyElement)element);
}
private int getLoadPathIndex(ISourceFolderRoot root) {
try {
IPath rootPath= root.getPath();
ISourceFolderRoot[] roots= root.getRubyProject().getSourceFolderRoots();
for (int i= 0; i < roots.length; i++) {
if (roots[i].getPath().equals(rootPath)) {
return i;
}
}
} catch (RubyModelException e) {
}
return Integer.MAX_VALUE;
}
}