KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > core > SortElementsOperation


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Alex Blewitt - alex_blewitt@yahoo.com https://bugs.eclipse.org/bugs/show_bug.cgi?id=171066
11  *******************************************************************************/

12 package org.eclipse.jdt.internal.core;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.Collections JavaDoc;
16 import java.util.Comparator JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19
20 import org.eclipse.jdt.core.IBuffer;
21 import org.eclipse.jdt.core.ICompilationUnit;
22 import org.eclipse.jdt.core.IJavaElement;
23 import org.eclipse.jdt.core.IJavaModelStatus;
24 import org.eclipse.jdt.core.IJavaModelStatusConstants;
25 import org.eclipse.jdt.core.JavaModelException;
26 import org.eclipse.jdt.core.compiler.CharOperation;
27 import org.eclipse.jdt.core.dom.ASTNode;
28 import org.eclipse.jdt.core.dom.ASTParser;
29 import org.eclipse.jdt.core.dom.ASTVisitor;
30 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
31 import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
32 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
33 import org.eclipse.jdt.core.dom.BodyDeclaration;
34 import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
35 import org.eclipse.jdt.core.dom.EnumDeclaration;
36 import org.eclipse.jdt.core.dom.TypeDeclaration;
37 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
38 import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
39 import org.eclipse.jdt.core.util.CompilationUnitSorter;
40 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
41 import org.eclipse.jdt.internal.core.util.Messages;
42 import org.eclipse.jface.text.BadLocationException;
43 import org.eclipse.jface.text.Document;
44 import org.eclipse.text.edits.RangeMarker;
45 import org.eclipse.text.edits.TextEdit;
46 import org.eclipse.text.edits.TextEditGroup;
47
48 /**
49  * This operation is used to sort elements in a compilation unit according to
50  * certain criteria.
51  *
52  * @since 2.1
53  */

54 public class SortElementsOperation extends JavaModelOperation {
55     public static final String JavaDoc CONTAINS_MALFORMED_NODES = "malformed"; //$NON-NLS-1$
56

57     Comparator JavaDoc comparator;
58     int[] positions;
59     int apiLevel;
60     
61     /**
62      * Constructor for SortElementsOperation.
63      *
64      * @param level the AST API level; one of the AST LEVEL constants
65      * @param elements
66      * @param positions
67      * @param comparator
68      */

69     public SortElementsOperation(int level, IJavaElement[] elements, int[] positions, Comparator JavaDoc comparator) {
70         super(elements);
71         this.comparator = comparator;
72         this.positions = positions;
73         this.apiLevel = level;
74     }
75
76     /**
77      * Returns the amount of work for the main task of this operation for
78      * progress reporting.
79      */

80     protected int getMainAmountOfWork(){
81         return this.elementsToProcess.length;
82     }
83     
84     boolean checkMalformedNodes(ASTNode node) {
85         Object JavaDoc property = node.getProperty(CONTAINS_MALFORMED_NODES);
86         if (property == null) return false;
87         return ((Boolean JavaDoc) property).booleanValue();
88     }
89
90     protected boolean isMalformed(ASTNode node) {
91         return (node.getFlags() & ASTNode.MALFORMED) != 0;
92     }
93
94     /**
95      * @see org.eclipse.jdt.internal.core.JavaModelOperation#executeOperation()
96      */

97     protected void executeOperation() throws JavaModelException {
98         try {
99             beginTask(Messages.operation_sortelements, getMainAmountOfWork());
100             CompilationUnit copy = (CompilationUnit) this.elementsToProcess[0];
101             ICompilationUnit unit = copy.getPrimary();
102             IBuffer buffer = copy.getBuffer();
103             if (buffer == null) {
104                 return;
105             }
106             char[] bufferContents = buffer.getCharacters();
107             String JavaDoc result = processElement(unit, bufferContents);
108             if (!CharOperation.equals(result.toCharArray(), bufferContents)) {
109                 copy.getBuffer().setContents(result);
110             }
111             worked(1);
112         } finally {
113             done();
114         }
115     }
116     
117     /**
118      * Calculates the required text edits to sort the <code>unit</code>
119      * @param group
120      * @return the edit or null if no sorting is required
121      */

122     public TextEdit calculateEdit(org.eclipse.jdt.core.dom.CompilationUnit unit, TextEditGroup group) throws JavaModelException {
123         if (this.elementsToProcess.length != 1)
124             throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS));
125         
126         if (!(this.elementsToProcess[0] instanceof ICompilationUnit))
127             throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, this.elementsToProcess[0]));
128         
129         try {
130             beginTask(Messages.operation_sortelements, getMainAmountOfWork());
131             
132             ICompilationUnit cu= (ICompilationUnit)this.elementsToProcess[0];
133             String JavaDoc content= cu.getBuffer().getContents();
134             ASTRewrite rewrite= sortCompilationUnit(unit, group);
135             if (rewrite == null) {
136                 return null;
137             }
138             
139             Document document= new Document(content);
140             return rewrite.rewriteAST(document, null);
141         } finally {
142             done();
143         }
144     }
145
146     /**
147      * Method processElement.
148      * @param unit
149      * @param source
150      */

151     private String JavaDoc processElement(ICompilationUnit unit, char[] source) {
152         Document document = new Document(new String JavaDoc(source));
153         CompilerOptions options = new CompilerOptions(unit.getJavaProject().getOptions(true));
154         ASTParser parser = ASTParser.newParser(this.apiLevel);
155         parser.setCompilerOptions(options.getMap());
156         parser.setSource(source);
157         parser.setKind(ASTParser.K_COMPILATION_UNIT);
158         parser.setResolveBindings(false);
159         org.eclipse.jdt.core.dom.CompilationUnit ast = (org.eclipse.jdt.core.dom.CompilationUnit) parser.createAST(null);
160         
161         ASTRewrite rewriter= sortCompilationUnit(ast, null);
162         if (rewriter == null)
163             return document.get();
164         
165         TextEdit edits = rewriter.rewriteAST(document, null);
166         
167         RangeMarker[] markers = null;
168         if (this.positions != null) {
169             markers = new RangeMarker[this.positions.length];
170             for (int i = 0, max = this.positions.length; i < max; i++) {
171                 markers[i]= new RangeMarker(this.positions[i], 0);
172                 insert(edits, markers[i]);
173             }
174         }
175         try {
176             edits.apply(document, TextEdit.UPDATE_REGIONS);
177             if (this.positions != null) {
178                 for (int i= 0, max = markers.length; i < max; i++) {
179                     this.positions[i]= markers[i].getOffset();
180                 }
181             }
182         } catch (BadLocationException e) {
183             // ignore
184
}
185         return document.get();
186     }
187     
188     
189     private ASTRewrite sortCompilationUnit(org.eclipse.jdt.core.dom.CompilationUnit ast, final TextEditGroup group) {
190         ast.accept(new ASTVisitor() {
191             public boolean visit(org.eclipse.jdt.core.dom.CompilationUnit compilationUnit) {
192                 List JavaDoc types = compilationUnit.types();
193                 for (Iterator JavaDoc iter = types.iterator(); iter.hasNext();) {
194                     AbstractTypeDeclaration typeDeclaration = (AbstractTypeDeclaration) iter.next();
195                     typeDeclaration.setProperty(CompilationUnitSorter.RELATIVE_ORDER, new Integer JavaDoc(typeDeclaration.getStartPosition()));
196                     compilationUnit.setProperty(CONTAINS_MALFORMED_NODES, Boolean.valueOf(isMalformed(typeDeclaration)));
197                 }
198                 return true;
199             }
200             public boolean visit(AnnotationTypeDeclaration annotationTypeDeclaration) {
201                 List JavaDoc bodyDeclarations = annotationTypeDeclaration.bodyDeclarations();
202                 for (Iterator JavaDoc iter = bodyDeclarations.iterator(); iter.hasNext();) {
203                     BodyDeclaration bodyDeclaration = (BodyDeclaration) iter.next();
204                     bodyDeclaration.setProperty(CompilationUnitSorter.RELATIVE_ORDER, new Integer JavaDoc(bodyDeclaration.getStartPosition()));
205                     annotationTypeDeclaration.setProperty(CONTAINS_MALFORMED_NODES, Boolean.valueOf(isMalformed(bodyDeclaration)));
206                 }
207                 return true;
208             }
209
210             public boolean visit(AnonymousClassDeclaration anonymousClassDeclaration) {
211                 List JavaDoc bodyDeclarations = anonymousClassDeclaration.bodyDeclarations();
212                 for (Iterator JavaDoc iter = bodyDeclarations.iterator(); iter.hasNext();) {
213                     BodyDeclaration bodyDeclaration = (BodyDeclaration) iter.next();
214                     bodyDeclaration.setProperty(CompilationUnitSorter.RELATIVE_ORDER, new Integer JavaDoc(bodyDeclaration.getStartPosition()));
215                     anonymousClassDeclaration.setProperty(CONTAINS_MALFORMED_NODES, Boolean.valueOf(isMalformed(bodyDeclaration)));
216                 }
217                 return true;
218             }
219             
220             public boolean visit(TypeDeclaration typeDeclaration) {
221                 List JavaDoc bodyDeclarations = typeDeclaration.bodyDeclarations();
222                 for (Iterator JavaDoc iter = bodyDeclarations.iterator(); iter.hasNext();) {
223                     BodyDeclaration bodyDeclaration = (BodyDeclaration) iter.next();
224                     bodyDeclaration.setProperty(CompilationUnitSorter.RELATIVE_ORDER, new Integer JavaDoc(bodyDeclaration.getStartPosition()));
225                     typeDeclaration.setProperty(CONTAINS_MALFORMED_NODES, Boolean.valueOf(isMalformed(bodyDeclaration)));
226                 }
227                 return true;
228             }
229
230             public boolean visit(EnumDeclaration enumDeclaration) {
231                 List JavaDoc bodyDeclarations = enumDeclaration.bodyDeclarations();
232                 for (Iterator JavaDoc iter = bodyDeclarations.iterator(); iter.hasNext();) {
233                     BodyDeclaration bodyDeclaration = (BodyDeclaration) iter.next();
234                     bodyDeclaration.setProperty(CompilationUnitSorter.RELATIVE_ORDER, new Integer JavaDoc(bodyDeclaration.getStartPosition()));
235                     enumDeclaration.setProperty(CONTAINS_MALFORMED_NODES, Boolean.valueOf(isMalformed(bodyDeclaration)));
236                 }
237                 List JavaDoc enumConstants = enumDeclaration.enumConstants();
238                 for (Iterator JavaDoc iter = enumConstants.iterator(); iter.hasNext();) {
239                     EnumConstantDeclaration enumConstantDeclaration = (EnumConstantDeclaration) iter.next();
240                     enumConstantDeclaration.setProperty(CompilationUnitSorter.RELATIVE_ORDER, new Integer JavaDoc(enumConstantDeclaration.getStartPosition()));
241                     enumDeclaration.setProperty(CONTAINS_MALFORMED_NODES, Boolean.valueOf(isMalformed(enumConstantDeclaration)));
242                 }
243                 return true;
244             }
245         });
246         
247         final ASTRewrite rewriter= ASTRewrite.create(ast.getAST());
248         final boolean[] hasChanges= new boolean[] {false};
249         
250         ast.accept(new ASTVisitor() {
251         
252             private void sortElements(List JavaDoc elements, ListRewrite listRewrite) {
253                 if (elements.size() == 0)
254                     return;
255                 
256                 final List JavaDoc myCopy = new ArrayList JavaDoc();
257                 myCopy.addAll(elements);
258                 Collections.sort(myCopy, SortElementsOperation.this.comparator);
259                 
260                 for (int i = 0; i < elements.size(); i++) {
261                     ASTNode oldNode= (ASTNode) elements.get(i);
262                     ASTNode newNode= (ASTNode) myCopy.get(i);
263                     if (oldNode != newNode) {
264                         listRewrite.replace(oldNode, rewriter.createMoveTarget(newNode), group);
265                         hasChanges[0]= true;
266                     }
267                 }
268             }
269
270             public boolean visit(org.eclipse.jdt.core.dom.CompilationUnit compilationUnit) {
271                 if (checkMalformedNodes(compilationUnit)) {
272                     return true; // abort sorting of current element
273
}
274                 
275                 sortElements(compilationUnit.types(), rewriter.getListRewrite(compilationUnit, org.eclipse.jdt.core.dom.CompilationUnit.TYPES_PROPERTY));
276                 return true;
277             }
278
279             public boolean visit(AnnotationTypeDeclaration annotationTypeDeclaration) {
280                 if (checkMalformedNodes(annotationTypeDeclaration)) {
281                     return true; // abort sorting of current element
282
}
283                 
284                 sortElements(annotationTypeDeclaration.bodyDeclarations(), rewriter.getListRewrite(annotationTypeDeclaration, AnnotationTypeDeclaration.BODY_DECLARATIONS_PROPERTY));
285                 return true;
286             }
287
288             public boolean visit(AnonymousClassDeclaration anonymousClassDeclaration) {
289                 if (checkMalformedNodes(anonymousClassDeclaration)) {
290                     return true; // abort sorting of current element
291
}
292                 
293                 sortElements(anonymousClassDeclaration.bodyDeclarations(), rewriter.getListRewrite(anonymousClassDeclaration, AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY));
294                 return true;
295             }
296             
297             public boolean visit(TypeDeclaration typeDeclaration) {
298                 if (checkMalformedNodes(typeDeclaration)) {
299                     return true; // abort sorting of current element
300
}
301                 
302                 sortElements(typeDeclaration.bodyDeclarations(), rewriter.getListRewrite(typeDeclaration, TypeDeclaration.BODY_DECLARATIONS_PROPERTY));
303                 return true;
304             }
305
306             public boolean visit(EnumDeclaration enumDeclaration) {
307                 if (checkMalformedNodes(enumDeclaration)) {
308                     return true; // abort sorting of current element
309
}
310                 
311                 sortElements(enumDeclaration.bodyDeclarations(), rewriter.getListRewrite(enumDeclaration, EnumDeclaration.BODY_DECLARATIONS_PROPERTY));
312                 sortElements(enumDeclaration.enumConstants(), rewriter.getListRewrite(enumDeclaration, EnumDeclaration.ENUM_CONSTANTS_PROPERTY));
313                 return true;
314             }
315         });
316         
317         if (!hasChanges[0])
318             return null;
319         
320         return rewriter;
321     }
322
323     /**
324      * Possible failures:
325      * <ul>
326      * <li>NO_ELEMENTS_TO_PROCESS - the compilation unit supplied to the operation is <code>null</code></li>.
327      * <li>INVALID_ELEMENT_TYPES - the supplied elements are not an instance of IWorkingCopy</li>.
328      * </ul>
329      * @return IJavaModelStatus
330      */

331     public IJavaModelStatus verify() {
332         if (this.elementsToProcess.length != 1) {
333             return new JavaModelStatus(IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
334         }
335         if (this.elementsToProcess[0] == null) {
336             return new JavaModelStatus(IJavaModelStatusConstants.NO_ELEMENTS_TO_PROCESS);
337         }
338         if (!(this.elementsToProcess[0] instanceof ICompilationUnit) || !((ICompilationUnit) this.elementsToProcess[0]).isWorkingCopy()) {
339             return new JavaModelStatus(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, this.elementsToProcess[0]);
340         }
341         return JavaModelStatus.VERIFIED_OK;
342     }
343     
344     public static void insert(TextEdit parent, TextEdit edit) {
345         if (!parent.hasChildren()) {
346             parent.addChild(edit);
347             return;
348         }
349         TextEdit[] children= parent.getChildren();
350         // First dive down to find the right parent.
351
for (int i= 0; i < children.length; i++) {
352             TextEdit child= children[i];
353             if (covers(child, edit)) {
354                 insert(child, edit);
355                 return;
356             }
357         }
358         // We have the right parent. Now check if some of the children have to
359
// be moved under the new edit since it is covering it.
360
for (int i= children.length - 1; i >= 0; i--) {
361             TextEdit child= children[i];
362             if (covers(edit, child)) {
363                 parent.removeChild(i);
364                 edit.addChild(child);
365             }
366         }
367         parent.addChild(edit);
368     }
369     
370     private static boolean covers(TextEdit thisEdit, TextEdit otherEdit) {
371         if (thisEdit.getLength() == 0) {
372             return false;
373         }
374         
375         int thisOffset= thisEdit.getOffset();
376         int thisEnd= thisEdit.getExclusiveEnd();
377         if (otherEdit.getLength() == 0) {
378             int otherOffset= otherEdit.getOffset();
379             return thisOffset <= otherOffset && otherOffset < thisEnd;
380         } else {
381             int otherOffset= otherEdit.getOffset();
382             int otherEnd= otherEdit.getExclusiveEnd();
383             return thisOffset <= otherOffset && otherEnd <= thisEnd;
384         }
385     }
386 }
387
Popular Tags