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 (+136 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
447
	 *                </ul>
448
	 * @exception IllegalArgumentException
449
	 *                if the given compilation unit is null or if the given
450
	 *                comparator is null, or if <code>options</code> is not one
451
	 *                of the supported levels.
452
	 * @see org.eclipse.jdt.core.dom.BodyDeclaration
453
	 * @see #RELATIVE_ORDER
454
	 * @since 3.3
455
	 */
456
	public static TextEdit sort(CompilationUnit unit,
457
			Comparator comparator,
458
			int options,
459
			TextEditGroup group,
460
			IProgressMonitor monitor) throws JavaModelException {
461
		if (unit == null || comparator == null) {
462
			throw new IllegalArgumentException();
463
		}
464
		
465
		SortElementsOperation operation = new SortElementsOperation(AST.JLS3, new IJavaElement[] {unit.getJavaElement()}, null, comparator);
466
		return operation.calculateEdit(unit, group);
467
	}
332
}
468
}
(-)model/org/eclipse/jdt/internal/core/SortElementsOperation.java (-100 / +98 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
		try {
124
			beginTask(Messages.operation_sortelements, getMainAmountOfWork());
125
			
126
			ASTRewrite rewrite= sortCompilationUnit(unit, group);
127
			if (rewrite == null)
128
				return null;
129
			
130
			ICompilationUnit cu= (ICompilationUnit)this.elementsToProcess[0];
131
			String content= cu.getBuffer().getContents();
132
			Document document= new Document(content);
133
			
134
			return rewrite.rewriteAST(document, null);
135
		} finally {
136
			done();
137
		}
138
	}
115
139
116
	/**
140
	/**
117
	 * Method processElement.
141
	 * Method processElement.
Lines 119-132 Link Here
119
	 * @param source
143
	 * @param source
120
	 */
144
	 */
121
	private String processElement(ICompilationUnit unit, char[] source) {
145
	private String processElement(ICompilationUnit unit, char[] source) {
146
		Document document = new Document(new String(source));
122
		CompilerOptions options = new CompilerOptions(unit.getJavaProject().getOptions(true));
147
		CompilerOptions options = new CompilerOptions(unit.getJavaProject().getOptions(true));
123
		ASTParser parser = ASTParser.newParser(this.apiLevel);
148
		ASTParser parser = ASTParser.newParser(this.apiLevel);
124
		parser.setCompilerOptions(options.getMap());
149
		parser.setCompilerOptions(options.getMap());
125
		parser.setSource(source);
150
		parser.setSource(source);
126
		parser.setKind(ASTParser.K_COMPILATION_UNIT);
151
		parser.setKind(ASTParser.K_COMPILATION_UNIT);
127
		parser.setResolveBindings(false);
152
		parser.setResolveBindings(false);
128
		org.eclipse.jdt.core.dom.CompilationUnit domUnit = (org.eclipse.jdt.core.dom.CompilationUnit) parser.createAST(null);
153
		org.eclipse.jdt.core.dom.CompilationUnit ast = (org.eclipse.jdt.core.dom.CompilationUnit) parser.createAST(null);
129
		domUnit.accept(new ASTVisitor() {
154
        
155
		ASTRewrite rewriter= sortCompilationUnit(ast, null);
156
		if (rewriter == null)
157
			return document.get();
158
		
159
		TextEdit edits = rewriter.rewriteAST(document, null);
160
		
161
		RangeMarker[] markers = null;
162
		if (this.positions != null) {
163
			markers = new RangeMarker[this.positions.length];
164
			for (int i = 0, max = this.positions.length; i < max; i++) {
165
				markers[i]= new RangeMarker(this.positions[i], 0);
166
				insert(edits, markers[i]);
167
			}
168
		}
169
		try {
170
			edits.apply(document, TextEdit.UPDATE_REGIONS);
171
			if (this.positions != null) {
172
				for (int i= 0, max = markers.length; i < max; i++) {
173
					this.positions[i]= markers[i].getOffset();
174
				}
175
			}
176
		} catch (BadLocationException e) {
177
			// ignore
178
		}
179
		return document.get();
180
	}
181
	
182
	
183
	private ASTRewrite sortCompilationUnit(org.eclipse.jdt.core.dom.CompilationUnit ast, final TextEditGroup group) {
184
		ast.accept(new ASTVisitor() {
130
			public boolean visit(org.eclipse.jdt.core.dom.CompilationUnit compilationUnit) {
185
			public boolean visit(org.eclipse.jdt.core.dom.CompilationUnit compilationUnit) {
131
				List types = compilationUnit.types();
186
				List types = compilationUnit.types();
132
				for (Iterator iter = types.iterator(); iter.hasNext();) {
187
				for (Iterator iter = types.iterator(); iter.hasNext();) {
Lines 182-233 Link Here
182
				return true;
237
				return true;
183
			}
238
			}
184
		});
239
		});
185
		final AST localAst = domUnit.getAST();
186
		final ASTRewrite rewriter = ASTRewrite.create(localAst);
187
		RangeMarker[] markers = null;
188
		
240
		
189
		final boolean needPositionsMapping = this.positions != null;
241
		final ASTRewrite rewriter= ASTRewrite.create(ast.getAST()); 
190
		if (needPositionsMapping) {
242
		final boolean[] hasChanges= new boolean[] {false}; 
191
			markers = new RangeMarker[this.positions.length];
243
		
192
			for (int i= 0; i < this.positions.length; i++) {
244
		ast.accept(new ASTVisitor() {
193
				markers[i]= new RangeMarker(this.positions[i], 0);
245
		
246
			private void sortElements(List elements, ListRewrite listRewrite) {
247
				if (elements.size() == 0)
248
					return;
249
				
250
				final List myCopy = new ArrayList();
251
				myCopy.addAll(elements);
252
				Collections.sort(myCopy, SortElementsOperation.this.comparator);
253
				
254
				for (int i = 0; i < elements.size(); i++) {
255
					ASTNode oldNode= (ASTNode) elements.get(i);
256
					ASTNode newNode= (ASTNode) myCopy.get(i);
257
					if (oldNode != newNode) {
258
						listRewrite.replace(oldNode, rewriter.createMoveTarget(newNode), group);
259
						hasChanges[0]= true;
260
					}
261
				}
194
			}
262
			}
195
		}
263
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) {
264
			public boolean visit(org.eclipse.jdt.core.dom.CompilationUnit compilationUnit) {
200
				if (checkMalformedNodes(compilationUnit)) {
265
				if (checkMalformedNodes(compilationUnit)) {
201
					return true; // abort sorting of current element
266
					return true; // abort sorting of current element
202
				}
267
				}
203
				ListRewrite listRewrite = rewriter.getListRewrite(compilationUnit, org.eclipse.jdt.core.dom.CompilationUnit.TYPES_PROPERTY);
268
				
204
				List types = compilationUnit.types();
269
				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;
270
				return true;
215
			}
271
			}
272
216
			public boolean visit(AnnotationTypeDeclaration annotationTypeDeclaration) {
273
			public boolean visit(AnnotationTypeDeclaration annotationTypeDeclaration) {
217
				if (checkMalformedNodes(annotationTypeDeclaration)) {
274
				if (checkMalformedNodes(annotationTypeDeclaration)) {
218
					return true; // abort sorting of current element
275
					return true; // abort sorting of current element
219
				}
276
				}
220
				ListRewrite listRewrite = rewriter.getListRewrite(annotationTypeDeclaration, AnnotationTypeDeclaration.BODY_DECLARATIONS_PROPERTY);
277
				
221
				List bodyDeclarations = annotationTypeDeclaration.bodyDeclarations();
278
				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;
279
				return true;
232
			}
280
			}
233
281
Lines 235-251 Link Here
235
				if (checkMalformedNodes(anonymousClassDeclaration)) {
283
				if (checkMalformedNodes(anonymousClassDeclaration)) {
236
					return true; // abort sorting of current element
284
					return true; // abort sorting of current element
237
				}
285
				}
238
				ListRewrite listRewrite = rewriter.getListRewrite(anonymousClassDeclaration, AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY);
286
				
239
				List bodyDeclarations = anonymousClassDeclaration.bodyDeclarations();
287
				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;
288
				return true;
250
			}
289
			}
251
			
290
			
Lines 253-269 Link Here
253
				if (checkMalformedNodes(typeDeclaration)) {
292
				if (checkMalformedNodes(typeDeclaration)) {
254
					return true; // abort sorting of current element
293
					return true; // abort sorting of current element
255
				}
294
				}
256
				ListRewrite listRewrite = rewriter.getListRewrite(typeDeclaration, TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
295
				
257
				List bodyDeclarations = typeDeclaration.bodyDeclarations();
296
				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;
297
				return true;
268
			}
298
			}
269
299
Lines 271-319 Link Here
271
				if (checkMalformedNodes(enumDeclaration)) {
301
				if (checkMalformedNodes(enumDeclaration)) {
272
					return true; // abort sorting of current element
302
					return true; // abort sorting of current element
273
				}
303
				}
274
				ListRewrite listRewrite = rewriter.getListRewrite(enumDeclaration, EnumDeclaration.BODY_DECLARATIONS_PROPERTY);
304
				
275
				List bodyDeclarations = enumDeclaration.bodyDeclarations();
305
				sortElements(enumDeclaration.bodyDeclarations(), rewriter.getListRewrite(enumDeclaration, EnumDeclaration.BODY_DECLARATIONS_PROPERTY));
276
				int length = bodyDeclarations.size();
306
				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;
307
				return true;
297
			}
308
			}
298
		});			
309
		});
299
		TextEdit edits = rewriter.rewriteAST(document, null);
310
		
300
		if (needPositionsMapping) {
311
		if (!hasChanges[0])
301
			for (int i = 0, max = markers.length; i < max; i++) {
312
			return null;
302
				insert(edits, markers[i]);
313
		
303
			}
314
		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
	}
315
	}
318
316
319
	/**
317
	/**

Return to bug 171066