View | Details | Raw Unified | Return to bug 171066 | Differences between
and this patch

Collapse All | Expand All

(-)model/org/eclipse/jdt/core/util/CompilationUnitSorter.java (+137 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Alex Blewitt - alex_blewitt@yahoo.com https://bugs.eclipse.org/bugs/show_bug.cgi?id=171066
10
 *******************************************************************************/
11
 *******************************************************************************/
11
12
12
package org.eclipse.jdt.core.util;
13
package org.eclipse.jdt.core.util;
Lines 15-23 Link Here
15
16
16
import org.eclipse.core.runtime.IProgressMonitor;
17
import org.eclipse.core.runtime.IProgressMonitor;
17
import org.eclipse.jdt.core.ICompilationUnit;
18
import org.eclipse.jdt.core.ICompilationUnit;
19
import org.eclipse.jdt.core.IJavaElement;
18
import org.eclipse.jdt.core.JavaModelException;
20
import org.eclipse.jdt.core.JavaModelException;
19
import org.eclipse.jdt.core.dom.AST;
21
import org.eclipse.jdt.core.dom.AST;
22
import org.eclipse.jdt.core.dom.CompilationUnit;
20
import org.eclipse.jdt.internal.core.SortElementsOperation;
23
import org.eclipse.jdt.internal.core.SortElementsOperation;
24
import org.eclipse.text.edits.TextEdit;
25
import org.eclipse.text.edits.TextEditGroup;
21
26
22
/**
27
/**
23
 * Operation for sorting members within a compilation unit.
28
 * Operation for sorting members within a compilation unit.
Lines 329-332 Link Here
329
        operation.runOperation(monitor);
334
        operation.runOperation(monitor);
330
    }
335
    }
331
    
336
    
337
	/**
338
	 * Reorders the declarations in the given compilation unit according to the
339
	 * specified comparator. The caller is responsible for arranging in advance
340
	 * that the given compilation unit is a working copy, and for applying the
341
	 * returned TextEdit afterwards.
342
	 * <p>
343
	 * <b>Note:</b> Reordering the members within a type declaration might be
344
	 * more than a cosmetic change and could have potentially serious
345
	 * repercussions. Firstly, the order in which the fields of a type are
346
	 * initialized is significant in the Java language; reordering fields and
347
	 * initializers may result in compilation errors or change the execution
348
	 * behavior of the code. Secondly, reordering a class's members may affect
349
	 * how its instances are serialized. This operation should therefore be used
350
	 * with caution and due concern for potential negative side effects.
351
	 * </p>
352
	 * <p>
353
	 * The <code>compare</code> method of the given comparator is passed pairs
354
	 * of body declarations (subclasses of <code>BodyDeclaration</code>)
355
	 * representing body declarations at the same level. The nodes are from an
356
	 * AST of the specified level ({@link org.eclipse.jdt.core.dom.ASTParser#newParser(int)}.
357
	 * Clients will generally use AST.JLS3 since that will cover all
358
	 * constructs found in Java 1.0, 1.1, 1.2, 1.3, 1.4, and 1.5 source code.
359
	 * The comparator is called on body declarations of nested classes,
360
	 * including anonymous and local classes, but always at the same level.
361
	 * Clients need to provide a comparator implementation (there is no standard
362
	 * comparator). The <code>RELATIVE_ORDER</code> property attached to these
363
	 * AST nodes affords the comparator a way to preserve the original relative
364
	 * order.
365
	 * </p>
366
	 * <p>
367
	 * The body declarations passed as parameters to the comparator always carry
368
	 * at least the following minimal signature information: <br>
369
	 * <table border="1" width="80%" cellpadding="5">
370
	 * <tr>
371
	 * <td width="20%"><code>TypeDeclaration</code></td>
372
	 * <td width="50%"><code>modifiers, isInterface, name, superclass,
373
	 *        superInterfaces, typeParameters<br>
374
	 *        RELATIVE_ORDER property</code></td>
375
	 * </tr>
376
	 * <tr>
377
	 * <td width="20%"><code>FieldDeclaration</code></td>
378
	 * <td width="50%"><code>modifiers, type, fragments
379
	 *        (VariableDeclarationFragments
380
	 *        with name only)<br>
381
	 *        RELATIVE_ORDER property</code></td>
382
	 * </tr>
383
	 * <tr>
384
	 * <td width="20%"><code>MethodDeclaration</code></td>
385
	 * <td width="50%"><code>modifiers, isConstructor, returnType, name,
386
	 *        typeParameters, parameters
387
	 *        (SingleVariableDeclarations with name, type, and modifiers only),
388
	 *        thrownExceptions<br>
389
	 *        RELATIVE_ORDER property</code></td>
390
	 * </tr>
391
	 * <tr>
392
	 * <td width="20%"><code>Initializer</code></td>
393
	 * <td width="50%"><code>modifiers<br>
394
	 *        RELATIVE_ORDER property</code></td>
395
	 * </tr>
396
	 * <tr>
397
	 * <td width="20%"><code>AnnotationTypeDeclaration</code></td>
398
	 * <td width="50%"><code>modifiers, name<br>
399
	 *        RELATIVE_ORDER property</code></td>
400
	 * </tr>
401
	 * <tr>
402
	 * <td width="20%"><code>AnnotationTypeMemberDeclaration</code></td>
403
	 * <td width="50%"><code>modifiers, name, type, default<br>
404
	 *        RELATIVE_ORDER property</code></td>
405
	 * </tr>
406
	 * <tr>
407
	 * <td width="20%"><code>EnumDeclaration</code></td>
408
	 * <td width="50%"><code>modifiers, name, superInterfaces<br>
409
	 *        RELATIVE_ORDER property</code></td>
410
	 * </tr>
411
	 * <tr>
412
	 * <td width="20%"><code>EnumConstantDeclaration</code></td>
413
	 * <td width="50%"><code>modifiers, name, arguments<br>
414
	 *        RELATIVE_ORDER property</code></td>
415
	 * </tr>
416
	 * </table> Clients should not rely on the AST nodes being properly parented
417
	 * or on having source range information. (Future releases may provide
418
	 * options for requesting additional information like source positions, full
419
	 * ASTs, non-recursive sorting, etc.)
420
	 * </p>
421
	 * 
422
	 * @param unit
423
	 *            the CompilationUnit to sort
424
	 * @param comparator
425
	 *            the comparator capable of ordering
426
	 *            <code>BodyDeclaration</code>s; this comparator is passed
427
	 *            AST nodes from an AST of the specified AST level
428
	 * @param options
429
	 *            bitwise-or of option flags; <code>0</code> for default
430
	 *            behavior (reserved for future growth)
431
	 * @param group
432
	 *            the text edit group to use when generating text edits, or <code>null</code>
433
	 * @param monitor
434
	 *            the progress monitor to notify, or <code>null</code> if none
435
	 * @return a TextEdit describing the required edits to do the sort, or <code>null</code> 
436
	 *            if sorting is not required
437
	 * @exception JavaModelException
438
	 *                if the compilation unit could not be sorted. Reasons
439
	 *                include:
440
	 *                <ul>
441
	 *                <li> The given compilation unit does not exist
442
	 *                (ELEMENT_DOES_NOT_EXIST)</li>
443
	 *                <li> The given compilation unit is not a working copy
444
	 *                (INVALID_ELEMENT_TYPES)</li>
445
	 *                <li> A <code>CoreException</code> occurred while
446
	 *                accessing the underlying resource</li>
447
	 *                <li>The given compilation unit doesn't come from an ICompilationUnit and this ICompilationUnit is
448
	 *                not a working copy (NO_ELEMENTS_TO_PROCESS)</li>
449
	 *                </ul>
450
	 * @exception IllegalArgumentException
451
	 *                if the given compilation unit is null or if the given
452
	 *                comparator is null, or if <code>options</code> is not one
453
	 *                of the supported levels.
454
	 * @see org.eclipse.jdt.core.dom.BodyDeclaration
455
	 * @see #RELATIVE_ORDER
456
	 * @since 3.3
457
	 */
458
	public static TextEdit sort(CompilationUnit unit,
459
			Comparator comparator,
460
			int options,
461
			TextEditGroup group,
462
			IProgressMonitor monitor) throws JavaModelException {
463
		if (unit == null || comparator == null) {
464
			throw new IllegalArgumentException();
465
		}
466
		SortElementsOperation operation = new SortElementsOperation(AST.JLS3, new IJavaElement[] { unit.getJavaElement() }, null, comparator);
467
		return operation.calculateEdit(unit, group);
468
	}
332
}
469
}
(-)model/org/eclipse/jdt/internal/core/SortElementsOperation.java (-100 / +102 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Alex Blewitt - alex_blewitt@yahoo.com https://bugs.eclipse.org/bugs/show_bug.cgi?id=171066
10
 *******************************************************************************/
11
 *******************************************************************************/
11
package org.eclipse.jdt.internal.core;
12
package org.eclipse.jdt.internal.core;
12
13
Lines 23-29 Link Here
23
import org.eclipse.jdt.core.IJavaModelStatusConstants;
24
import org.eclipse.jdt.core.IJavaModelStatusConstants;
24
import org.eclipse.jdt.core.JavaModelException;
25
import org.eclipse.jdt.core.JavaModelException;
25
import org.eclipse.jdt.core.compiler.CharOperation;
26
import org.eclipse.jdt.core.compiler.CharOperation;
26
import org.eclipse.jdt.core.dom.AST;
27
import org.eclipse.jdt.core.dom.ASTNode;
27
import org.eclipse.jdt.core.dom.ASTNode;
28
import org.eclipse.jdt.core.dom.ASTParser;
28
import org.eclipse.jdt.core.dom.ASTParser;
29
import org.eclipse.jdt.core.dom.ASTVisitor;
29
import org.eclipse.jdt.core.dom.ASTVisitor;
Lines 43-48 Link Here
43
import org.eclipse.jface.text.Document;
43
import org.eclipse.jface.text.Document;
44
import org.eclipse.text.edits.RangeMarker;
44
import org.eclipse.text.edits.RangeMarker;
45
import org.eclipse.text.edits.TextEdit;
45
import org.eclipse.text.edits.TextEdit;
46
import org.eclipse.text.edits.TextEditGroup;
46
47
47
/**
48
/**
48
 * This operation is used to sort elements in a compilation unit according to
49
 * This operation is used to sort elements in a compilation unit according to
Lines 112-117 Link Here
112
			done();
113
			done();
113
		}
114
		}
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
		IJavaModelStatus status= this.verify();
124
		if (!status.isOK()) {
125
			throw new JavaModelException(status);
126
		}
127
		try {
128
			beginTask(Messages.operation_sortelements, getMainAmountOfWork());
129
			
130
			ICompilationUnit cu= (ICompilationUnit)this.elementsToProcess[0];
131
			String content= cu.getBuffer().getContents();
132
			ASTRewrite rewrite= sortCompilationUnit(unit, group);
133
			if (rewrite == null) {
134
				return null;
135
			}
136
			
137
			Document document= new Document(content);
138
			return rewrite.rewriteAST(document, null);
139
		} finally {
140
			done();
141
		}
142
	}
115
143
116
	/**
144
	/**
117
	 * Method processElement.
145
	 * Method processElement.
Lines 119-132 Link Here
119
	 * @param source
147
	 * @param source
120
	 */
148
	 */
121
	private String processElement(ICompilationUnit unit, char[] source) {
149
	private String processElement(ICompilationUnit unit, char[] source) {
150
		Document document = new Document(new String(source));
122
		CompilerOptions options = new CompilerOptions(unit.getJavaProject().getOptions(true));
151
		CompilerOptions options = new CompilerOptions(unit.getJavaProject().getOptions(true));
123
		ASTParser parser = ASTParser.newParser(this.apiLevel);
152
		ASTParser parser = ASTParser.newParser(this.apiLevel);
124
		parser.setCompilerOptions(options.getMap());
153
		parser.setCompilerOptions(options.getMap());
125
		parser.setSource(source);
154
		parser.setSource(source);
126
		parser.setKind(ASTParser.K_COMPILATION_UNIT);
155
		parser.setKind(ASTParser.K_COMPILATION_UNIT);
127
		parser.setResolveBindings(false);
156
		parser.setResolveBindings(false);
128
		org.eclipse.jdt.core.dom.CompilationUnit domUnit = (org.eclipse.jdt.core.dom.CompilationUnit) parser.createAST(null);
157
		org.eclipse.jdt.core.dom.CompilationUnit ast = (org.eclipse.jdt.core.dom.CompilationUnit) parser.createAST(null);
129
		domUnit.accept(new ASTVisitor() {
158
        
159
		ASTRewrite rewriter= sortCompilationUnit(ast, null);
160
		if (rewriter == null)
161
			return document.get();
162
		
163
		TextEdit edits = rewriter.rewriteAST(document, null);
164
		
165
		RangeMarker[] markers = null;
166
		if (this.positions != null) {
167
			markers = new RangeMarker[this.positions.length];
168
			for (int i = 0, max = this.positions.length; i < max; i++) {
169
				markers[i]= new RangeMarker(this.positions[i], 0);
170
				insert(edits, markers[i]);
171
			}
172
		}
173
		try {
174
			edits.apply(document, TextEdit.UPDATE_REGIONS);
175
			if (this.positions != null) {
176
				for (int i= 0, max = markers.length; i < max; i++) {
177
					this.positions[i]= markers[i].getOffset();
178
				}
179
			}
180
		} catch (BadLocationException e) {
181
			// ignore
182
		}
183
		return document.get();
184
	}
185
	
186
	
187
	private ASTRewrite sortCompilationUnit(org.eclipse.jdt.core.dom.CompilationUnit ast, final TextEditGroup group) {
188
		ast.accept(new ASTVisitor() {
130
			public boolean visit(org.eclipse.jdt.core.dom.CompilationUnit compilationUnit) {
189
			public boolean visit(org.eclipse.jdt.core.dom.CompilationUnit compilationUnit) {
131
				List types = compilationUnit.types();
190
				List types = compilationUnit.types();
132
				for (Iterator iter = types.iterator(); iter.hasNext();) {
191
				for (Iterator iter = types.iterator(); iter.hasNext();) {
Lines 182-233 Link Here
182
				return true;
241
				return true;
183
			}
242
			}
184
		});
243
		});
185
		final AST localAst = domUnit.getAST();
186
		final ASTRewrite rewriter = ASTRewrite.create(localAst);
187
		RangeMarker[] markers = null;
188
		
244
		
189
		final boolean needPositionsMapping = this.positions != null;
245
		final ASTRewrite rewriter= ASTRewrite.create(ast.getAST()); 
190
		if (needPositionsMapping) {
246
		final boolean[] hasChanges= new boolean[] {false}; 
191
			markers = new RangeMarker[this.positions.length];
247
		
192
			for (int i= 0; i < this.positions.length; i++) {
248
		ast.accept(new ASTVisitor() {
193
				markers[i]= new RangeMarker(this.positions[i], 0);
249
		
250
			private void sortElements(List elements, ListRewrite listRewrite) {
251
				if (elements.size() == 0)
252
					return;
253
				
254
				final List myCopy = new ArrayList();
255
				myCopy.addAll(elements);
256
				Collections.sort(myCopy, SortElementsOperation.this.comparator);
257
				
258
				for (int i = 0; i < elements.size(); i++) {
259
					ASTNode oldNode= (ASTNode) elements.get(i);
260
					ASTNode newNode= (ASTNode) myCopy.get(i);
261
					if (oldNode != newNode) {
262
						listRewrite.replace(oldNode, rewriter.createMoveTarget(newNode), group);
263
						hasChanges[0]= true;
264
					}
265
				}
194
			}
266
			}
195
		}
267
196
		String generatedSource = new String(source);
197
		Document document = new Document(generatedSource);
198
		domUnit.accept(new ASTVisitor() {
199
			public boolean visit(org.eclipse.jdt.core.dom.CompilationUnit compilationUnit) {
268
			public boolean visit(org.eclipse.jdt.core.dom.CompilationUnit compilationUnit) {
200
				if (checkMalformedNodes(compilationUnit)) {
269
				if (checkMalformedNodes(compilationUnit)) {
201
					return true; // abort sorting of current element
270
					return true; // abort sorting of current element
202
				}
271
				}
203
				ListRewrite listRewrite = rewriter.getListRewrite(compilationUnit, org.eclipse.jdt.core.dom.CompilationUnit.TYPES_PROPERTY);
272
				
204
				List types = compilationUnit.types();
273
				sortElements(compilationUnit.types(), rewriter.getListRewrite(compilationUnit, org.eclipse.jdt.core.dom.CompilationUnit.TYPES_PROPERTY));
205
				final int length = types.size();
206
				if (length > 1) {
207
					final List myCopy = new ArrayList();
208
					myCopy.addAll(types);
209
					Collections.sort(myCopy, SortElementsOperation.this.comparator);
210
					for (int i = 0; i < length; i++) {
211
						listRewrite.replace((ASTNode) types.get(i), rewriter.createMoveTarget((ASTNode) myCopy.get(i)), null);
212
					}
213
				}
214
				return true;
274
				return true;
215
			}
275
			}
276
216
			public boolean visit(AnnotationTypeDeclaration annotationTypeDeclaration) {
277
			public boolean visit(AnnotationTypeDeclaration annotationTypeDeclaration) {
217
				if (checkMalformedNodes(annotationTypeDeclaration)) {
278
				if (checkMalformedNodes(annotationTypeDeclaration)) {
218
					return true; // abort sorting of current element
279
					return true; // abort sorting of current element
219
				}
280
				}
220
				ListRewrite listRewrite = rewriter.getListRewrite(annotationTypeDeclaration, AnnotationTypeDeclaration.BODY_DECLARATIONS_PROPERTY);
281
				
221
				List bodyDeclarations = annotationTypeDeclaration.bodyDeclarations();
282
				sortElements(annotationTypeDeclaration.bodyDeclarations(), rewriter.getListRewrite(annotationTypeDeclaration, AnnotationTypeDeclaration.BODY_DECLARATIONS_PROPERTY));
222
				final int length = bodyDeclarations.size();
223
				if (length > 1) {
224
					final List myCopy = new ArrayList();
225
					myCopy.addAll(bodyDeclarations);
226
					Collections.sort(myCopy, SortElementsOperation.this.comparator);
227
					for (int i = 0; i < length; i++) {
228
						listRewrite.replace((ASTNode) bodyDeclarations.get(i), rewriter.createMoveTarget((ASTNode) myCopy.get(i)), null);
229
					}
230
				}
231
				return true;
283
				return true;
232
			}
284
			}
233
285
Lines 235-251 Link Here
235
				if (checkMalformedNodes(anonymousClassDeclaration)) {
287
				if (checkMalformedNodes(anonymousClassDeclaration)) {
236
					return true; // abort sorting of current element
288
					return true; // abort sorting of current element
237
				}
289
				}
238
				ListRewrite listRewrite = rewriter.getListRewrite(anonymousClassDeclaration, AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY);
290
				
239
				List bodyDeclarations = anonymousClassDeclaration.bodyDeclarations();
291
				sortElements(anonymousClassDeclaration.bodyDeclarations(), rewriter.getListRewrite(anonymousClassDeclaration, AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY));	
240
				final int length = bodyDeclarations.size();
241
				if (length > 1) {
242
					final List myCopy = new ArrayList();
243
					myCopy.addAll(bodyDeclarations);
244
					Collections.sort(myCopy, SortElementsOperation.this.comparator);
245
					for (int i = 0; i < length; i++) {
246
						listRewrite.replace((ASTNode) bodyDeclarations.get(i), rewriter.createMoveTarget((ASTNode) myCopy.get(i)), null);
247
					}
248
				}
249
				return true;
292
				return true;
250
			}
293
			}
251
			
294
			
Lines 253-269 Link Here
253
				if (checkMalformedNodes(typeDeclaration)) {
296
				if (checkMalformedNodes(typeDeclaration)) {
254
					return true; // abort sorting of current element
297
					return true; // abort sorting of current element
255
				}
298
				}
256
				ListRewrite listRewrite = rewriter.getListRewrite(typeDeclaration, TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
299
				
257
				List bodyDeclarations = typeDeclaration.bodyDeclarations();
300
				sortElements(typeDeclaration.bodyDeclarations(), rewriter.getListRewrite(typeDeclaration, TypeDeclaration.BODY_DECLARATIONS_PROPERTY));
258
				final int length = bodyDeclarations.size();
259
				if (length > 1) {
260
					final List myCopy = new ArrayList();
261
					myCopy.addAll(bodyDeclarations);
262
					Collections.sort(myCopy, SortElementsOperation.this.comparator);
263
					for (int i = 0; i < length; i++) {
264
						listRewrite.replace((ASTNode) bodyDeclarations.get(i), rewriter.createMoveTarget((ASTNode) myCopy.get(i)), null);
265
					}
266
				}
267
				return true;
301
				return true;
268
			}
302
			}
269
303
Lines 271-319 Link Here
271
				if (checkMalformedNodes(enumDeclaration)) {
305
				if (checkMalformedNodes(enumDeclaration)) {
272
					return true; // abort sorting of current element
306
					return true; // abort sorting of current element
273
				}
307
				}
274
				ListRewrite listRewrite = rewriter.getListRewrite(enumDeclaration, EnumDeclaration.BODY_DECLARATIONS_PROPERTY);
308
				
275
				List bodyDeclarations = enumDeclaration.bodyDeclarations();
309
				sortElements(enumDeclaration.bodyDeclarations(), rewriter.getListRewrite(enumDeclaration, EnumDeclaration.BODY_DECLARATIONS_PROPERTY));
276
				int length = bodyDeclarations.size();
310
				sortElements(enumDeclaration.enumConstants(), rewriter.getListRewrite(enumDeclaration, EnumDeclaration.ENUM_CONSTANTS_PROPERTY));
277
				if (length > 1) {
278
					final List myCopy = new ArrayList();
279
					myCopy.addAll(bodyDeclarations);
280
					Collections.sort(myCopy, SortElementsOperation.this.comparator);
281
					for (int i = 0; i < length; i++) {
282
						listRewrite.replace((ASTNode) bodyDeclarations.get(i), rewriter.createMoveTarget((ASTNode) myCopy.get(i)), null);
283
					}
284
				}
285
				listRewrite = rewriter.getListRewrite(enumDeclaration, EnumDeclaration.ENUM_CONSTANTS_PROPERTY);
286
				List enumConstants = enumDeclaration.enumConstants();
287
				length = enumConstants.size();
288
				if (length > 1) {
289
					final List myCopy = new ArrayList();
290
					myCopy.addAll(enumConstants);
291
					Collections.sort(myCopy, SortElementsOperation.this.comparator);
292
					for (int i = 0; i < length; i++) {
293
						listRewrite.replace((ASTNode) enumConstants.get(i), rewriter.createMoveTarget((ASTNode) myCopy.get(i)), null);
294
					}
295
				}
296
				return true;
311
				return true;
297
			}
312
			}
298
		});			
313
		});
299
		TextEdit edits = rewriter.rewriteAST(document, null);
314
		
300
		if (needPositionsMapping) {
315
		if (!hasChanges[0])
301
			for (int i = 0, max = markers.length; i < max; i++) {
316
			return null;
302
				insert(edits, markers[i]);
317
		
303
			}
318
		return rewriter;
304
		}
305
		try {
306
			edits.apply(document, TextEdit.UPDATE_REGIONS);
307
			generatedSource = document.get();
308
			if (needPositionsMapping) {
309
				for (int i= 0, max = markers.length; i < max; i++) {
310
					this.positions[i]= markers[i].getOffset();
311
				}
312
			}
313
		} catch (BadLocationException e) {
314
			// ignore
315
		}
316
		return generatedSource;
317
	}
319
	}
318
320
319
	/**
321
	/**

Return to bug 171066