blob: b204c57f33ddb089e6488121b47597bec6c23b09 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2014 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
*******************************************************************************/
package org.eclipse.jdt.internal.corext.codemanipulation;
import java.util.Comparator;
import java.util.List;
import com.ibm.icu.text.Collator;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.util.CompilationUnitSorter;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
import org.eclipse.jdt.internal.ui.preferences.MembersOrderPreferenceCache;
/**
* Orders members is a compilation unit. A working copy must be passed.
*/
public class SortMembersOperation implements IWorkspaceRunnable {
/**
* Default comparator for body declarations.
*/
public static class DefaultJavaElementComparator implements Comparator<BodyDeclaration> {
private final Collator fCollator;
private final MembersOrderPreferenceCache fMemberOrderCache;
private final boolean fDoNotSortFields;
public DefaultJavaElementComparator(boolean doNotSortFields) {
fDoNotSortFields= doNotSortFields;
fCollator= Collator.getInstance();
fMemberOrderCache= JavaPlugin.getDefault().getMemberOrderPreferenceCache();
}
private int category(BodyDeclaration bodyDeclaration) {
switch (bodyDeclaration.getNodeType()) {
case ASTNode.METHOD_DECLARATION:
{
MethodDeclaration method= (MethodDeclaration) bodyDeclaration;
if (method.isConstructor()) {
return MembersOrderPreferenceCache.CONSTRUCTORS_INDEX;
}
int flags= method.getModifiers();
if (Modifier.isStatic(flags))
return MembersOrderPreferenceCache.STATIC_METHODS_INDEX;
else
return MembersOrderPreferenceCache.METHOD_INDEX;
}
case ASTNode.FIELD_DECLARATION :
{
if (JdtFlags.isStatic(bodyDeclaration))
return MembersOrderPreferenceCache.STATIC_FIELDS_INDEX;
else
return MembersOrderPreferenceCache.FIELDS_INDEX;
}
case ASTNode.INITIALIZER :
{
int flags= ((Initializer) bodyDeclaration).getModifiers();
if (Modifier.isStatic(flags))
return MembersOrderPreferenceCache.STATIC_INIT_INDEX;
else
return MembersOrderPreferenceCache.INIT_INDEX;
}
case ASTNode.TYPE_DECLARATION :
case ASTNode.ENUM_DECLARATION :
case ASTNode.ANNOTATION_TYPE_DECLARATION :
return MembersOrderPreferenceCache.TYPE_INDEX;
case ASTNode.ENUM_CONSTANT_DECLARATION :
return MembersOrderPreferenceCache.ENUM_CONSTANTS_INDEX;
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:
return MembersOrderPreferenceCache.METHOD_INDEX; // reusing the method index
}
return 0; // should never happen
}
private int getCategoryIndex(int category) {
return fMemberOrderCache.getCategoryIndex(category);
}
/**
* This comparator follows the contract defined in CompilationUnitSorter.sort.
* @see Comparator#compare(java.lang.Object, java.lang.Object)
* @see CompilationUnitSorter#sort(int, org.eclipse.jdt.core.ICompilationUnit, int[], java.util.Comparator, int, org.eclipse.core.runtime.IProgressMonitor)
*/
public int compare(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2) {
boolean preserved1= fDoNotSortFields && isSortPreserved(bodyDeclaration1);
boolean preserved2= fDoNotSortFields && isSortPreserved(bodyDeclaration2);
// Bug 407759: need to use a common category for all isSortPreserved members that are to be sorted in the same group:
int cat1= category(bodyDeclaration1);
if (preserved1) {
cat1= sortPreservedCategory(cat1);
}
int cat2= category(bodyDeclaration2);
if (preserved2) {
cat2= sortPreservedCategory(cat2);
}
if (cat1 != cat2) {
return getCategoryIndex(cat1) - getCategoryIndex(cat2);
}
// cat1 == cat2 implies preserved1 == preserved2
if (preserved1) {
return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2);
}
if (fMemberOrderCache.isSortByVisibility()) {
int flags1= JdtFlags.getVisibilityCode(bodyDeclaration1);
int flags2= JdtFlags.getVisibilityCode(bodyDeclaration2);
int vis= fMemberOrderCache.getVisibilityIndex(flags1) - fMemberOrderCache.getVisibilityIndex(flags2);
if (vis != 0) {
return vis;
}
}
switch (bodyDeclaration1.getNodeType()) {
case ASTNode.METHOD_DECLARATION :
{
MethodDeclaration method1= (MethodDeclaration) bodyDeclaration1;
MethodDeclaration method2= (MethodDeclaration) bodyDeclaration2;
if (fMemberOrderCache.isSortByVisibility()) {
int vis= fMemberOrderCache.getVisibilityIndex(method1.getModifiers()) - fMemberOrderCache.getVisibilityIndex(method2.getModifiers());
if (vis != 0) {
return vis;
}
}
String name1= method1.getName().getIdentifier();
String name2= method2.getName().getIdentifier();
// method declarations (constructors) are sorted by name
int cmp= this.fCollator.compare(name1, name2);
if (cmp != 0) {
return cmp;
}
// if names equal, sort by parameter types
List<SingleVariableDeclaration> parameters1= method1.parameters();
List<SingleVariableDeclaration> parameters2= method2.parameters();
int length1= parameters1.size();
int length2= parameters2.size();
int len= Math.min(length1, length2);
for (int i= 0; i < len; i++) {
SingleVariableDeclaration param1= parameters1.get(i);
SingleVariableDeclaration param2= parameters2.get(i);
cmp= this.fCollator.compare(buildSignature(param1.getType()), buildSignature(param2.getType()));
if (cmp != 0) {
return cmp;
}
}
if (length1 != length2) {
return length1 - length2;
}
return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2);
}
case ASTNode.FIELD_DECLARATION :
{
FieldDeclaration field1= (FieldDeclaration) bodyDeclaration1;
FieldDeclaration field2= (FieldDeclaration) bodyDeclaration2;
String name1= ((VariableDeclarationFragment) field1.fragments().get(0)).getName().getIdentifier();
String name2= ((VariableDeclarationFragment) field2.fragments().get(0)).getName().getIdentifier();
// field declarations are sorted by name
return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2);
}
case ASTNode.INITIALIZER :
{
// preserve relative order
return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2);
}
case ASTNode.TYPE_DECLARATION :
case ASTNode.ENUM_DECLARATION :
case ASTNode.ANNOTATION_TYPE_DECLARATION :
{
AbstractTypeDeclaration type1= (AbstractTypeDeclaration) bodyDeclaration1;
AbstractTypeDeclaration type2= (AbstractTypeDeclaration) bodyDeclaration2;
String name1= type1.getName().getIdentifier();
String name2= type2.getName().getIdentifier();
// typedeclarations are sorted by name
return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2);
}
case ASTNode.ENUM_CONSTANT_DECLARATION :
{
EnumConstantDeclaration decl1= (EnumConstantDeclaration) bodyDeclaration1;
EnumConstantDeclaration decl2= (EnumConstantDeclaration) bodyDeclaration2;
String name1= decl1.getName().getIdentifier();
String name2= decl2.getName().getIdentifier();
// enum constants declarations are sorted by name
return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2);
}
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION :
{
AnnotationTypeMemberDeclaration decl1= (AnnotationTypeMemberDeclaration) bodyDeclaration1;
AnnotationTypeMemberDeclaration decl2= (AnnotationTypeMemberDeclaration) bodyDeclaration2;
String name1= decl1.getName().getIdentifier();
String name2= decl2.getName().getIdentifier();
// enum constants declarations are sorted by name
return compareNames(bodyDeclaration1, bodyDeclaration2, name1, name2);
}
}
return 0;
}
private static int sortPreservedCategory(int category) {
switch (category) {
case MembersOrderPreferenceCache.STATIC_FIELDS_INDEX:
case MembersOrderPreferenceCache.STATIC_INIT_INDEX:
return MembersOrderPreferenceCache.STATIC_FIELDS_INDEX;
case MembersOrderPreferenceCache.FIELDS_INDEX:
case MembersOrderPreferenceCache.INIT_INDEX:
return MembersOrderPreferenceCache.FIELDS_INDEX;
default:
return category;
}
}
private boolean isSortPreserved(BodyDeclaration bodyDeclaration) {
switch (bodyDeclaration.getNodeType()) {
case ASTNode.FIELD_DECLARATION:
case ASTNode.ENUM_CONSTANT_DECLARATION:
case ASTNode.INITIALIZER:
return true;
default:
return false;
}
}
private int preserveRelativeOrder(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2) {
int value1= ((Integer) bodyDeclaration1.getProperty(CompilationUnitSorter.RELATIVE_ORDER)).intValue();
int value2= ((Integer) bodyDeclaration2.getProperty(CompilationUnitSorter.RELATIVE_ORDER)).intValue();
return value1 - value2;
}
private int compareNames(BodyDeclaration bodyDeclaration1, BodyDeclaration bodyDeclaration2, String name1, String name2) {
int cmp= this.fCollator.compare(name1, name2);
if (cmp != 0) {
return cmp;
}
return preserveRelativeOrder(bodyDeclaration1, bodyDeclaration2);
}
private String buildSignature(Type type) {
return ASTNodes.asString(type);
}
}
private ICompilationUnit fCompilationUnit;
private int[] fPositions;
private final boolean fDoNotSortFields;
/**
* Creates the operation.
* @param cu The working copy of a compilation unit.
* @param positions Positions to track or <code>null</code> if no positions
* should be tracked.
* @param doNotSortFields no fields and enum constants are sorted if true
*/
public SortMembersOperation(ICompilationUnit cu, int[] positions, boolean doNotSortFields) {
fCompilationUnit= cu;
fPositions= positions;
fDoNotSortFields= doNotSortFields;
}
/**
* Runs the operation.
* @param monitor a monitor to use to report progress
* @throws CoreException if the compilation unit could not be
* sorted. Reasons include:
* <ul>
* <li> The given compilation unit does not exist (ELEMENT_DOES_NOT_EXIST)</li>
* <li> The given compilation unit is not a working copy (INVALID_ELEMENT_TYPES)</li>
* <li> A <code>CoreException</code> occurred while accessing the underlying
* resource
* </ul>
*/
public void run(IProgressMonitor monitor) throws CoreException {
CompilationUnitSorter.sort(ASTProvider.SHARED_AST_LEVEL, fCompilationUnit, fPositions, new DefaultJavaElementComparator(fDoNotSortFields), 0, monitor);
}
/**
* @return Returns the scheduling rule for this operation
*/
public ISchedulingRule getScheduleRule() {
return ResourcesPlugin.getWorkspace().getRoot();
}
}