diff --git a/doc/positions.md b/doc/positions.md new file mode 100644 index 00000000000..143ba07b23d --- /dev/null +++ b/doc/positions.md @@ -0,0 +1,16 @@ +--- +title: Source Position +keywords: position +last_updated: November 21, 2016 +--- + +`SourcePosition` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/cu/SourcePosition.html)) defines the position of the `CtElement` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/declaration/CtElement.html)) in the original source file. +SourcePosition is extended by three specialized position : + +- `DeclarationSourcePosition` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/cu/position/DeclarationSourcePosition.html)) +- `BodyHolderSourcePosition` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/cu/position/BodyHolderSourcePosition.html)). + +These three specialisation are used to define the position of specific CtElement. +For example DeclarationSourcePosition is used to define the position of all declarations (variable, type, method, ...). +This provide an easy access to the position of the modifiers and the name. +The BodyHolderSourcePosition is used to declare the position of all elements that have a body (type, method, constructor) and it provides an access to the body position. \ No newline at end of file diff --git a/src/main/java/spoon/reflect/cu/SourcePosition.java b/src/main/java/spoon/reflect/cu/SourcePosition.java index 9797de0188b..8884d79d4f9 100644 --- a/src/main/java/spoon/reflect/cu/SourcePosition.java +++ b/src/main/java/spoon/reflect/cu/SourcePosition.java @@ -16,14 +16,64 @@ */ package spoon.reflect.cu; -import java.io.File; - import spoon.compiler.Environment; +import java.io.File; +import java.io.Serializable; + /** * This interface represents the position of a program element in a source file. */ -public interface SourcePosition { +public interface SourcePosition extends Serializable { + + SourcePosition NOPOSITION = new SourcePosition() { + private static final long serialVersionUID = 1L; + + @Override + public File getFile() { + return null; + } + + @Override + public CompilationUnit getCompilationUnit() { + return null; + } + + @Override + public int getLine() { + return -1; + } + + @Override + public int getEndLine() { + return -1; + } + + @Override + public int getColumn() { + return -1; + } + + @Override + public int getEndColumn() { + return -1; + } + + @Override + public int getSourceEnd() { + return -1; + } + + @Override + public int getSourceStart() { + return -1; + } + + @Override + public String toString() { + return "(unknown file)"; + } + }; /** * Returns a string representation of this position in the form @@ -79,9 +129,4 @@ public interface SourcePosition { * Gets the index at which the position starts in the source file. */ int getSourceStart(); - - /** - * Gets the index at which the name of the element position starts in the source file. - */ - int getNameSourceStart(); } diff --git a/src/main/java/spoon/reflect/cu/position/BodyHolderSourcePosition.java b/src/main/java/spoon/reflect/cu/position/BodyHolderSourcePosition.java new file mode 100644 index 00000000000..10f51f8cf7d --- /dev/null +++ b/src/main/java/spoon/reflect/cu/position/BodyHolderSourcePosition.java @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2006-2016 INRIA and contributors + * Spoon - http://spoon.gforge.inria.fr/ + * + * This software is governed by the CeCILL-C License under French law and + * abiding by the rules of distribution of free software. You can use, modify + * and/or redistribute the software under the terms of the CeCILL-C license as + * circulated by CEA, CNRS and INRIA at http://www.cecill.info. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package spoon.reflect.cu.position; + +/** + * This interface represents the position of a Method declaration in a source file. + */ +public interface BodyHolderSourcePosition extends DeclarationSourcePosition { + + int getBodyStart(); + + int getBodyEnd(); + +} diff --git a/src/main/java/spoon/reflect/cu/position/DeclarationSourcePosition.java b/src/main/java/spoon/reflect/cu/position/DeclarationSourcePosition.java new file mode 100644 index 00000000000..1e3a87a494d --- /dev/null +++ b/src/main/java/spoon/reflect/cu/position/DeclarationSourcePosition.java @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2006-2016 INRIA and contributors + * Spoon - http://spoon.gforge.inria.fr/ + * + * This software is governed by the CeCILL-C License under French law and + * abiding by the rules of distribution of free software. You can use, modify + * and/or redistribute the software under the terms of the CeCILL-C license as + * circulated by CEA, CNRS and INRIA at http://www.cecill.info. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package spoon.reflect.cu.position; + +import spoon.reflect.cu.SourcePosition; + +/** + * This interface represents the position of a program element in a source file. + */ +public interface DeclarationSourcePosition extends SourcePosition { + + int getModifierSourceStart(); + + int getModifierSourceEnd(); + + int getNameStart(); + + int getNameEnd(); + +} diff --git a/src/main/java/spoon/reflect/factory/CoreFactory.java b/src/main/java/spoon/reflect/factory/CoreFactory.java index 9f1fdd5b7fb..6cc33903e60 100644 --- a/src/main/java/spoon/reflect/factory/CoreFactory.java +++ b/src/main/java/spoon/reflect/factory/CoreFactory.java @@ -63,6 +63,8 @@ import spoon.reflect.code.CtVariableWrite; import spoon.reflect.code.CtWhile; import spoon.reflect.cu.CompilationUnit; +import spoon.reflect.cu.position.DeclarationSourcePosition; +import spoon.reflect.cu.position.BodyHolderSourcePosition; import spoon.reflect.cu.SourcePosition; import spoon.reflect.declaration.CtAnnotation; import spoon.reflect.declaration.CtAnnotationMethod; @@ -376,7 +378,27 @@ public interface CoreFactory { */ SourcePosition createSourcePosition( CompilationUnit compilationUnit, - int startDeclaration, int startSource, int end, int[] lineSeparatorPositions); + int startSource, int end, int[] lineSeparatorPositions); + + /** + * Creates a declaration source position. + */ + DeclarationSourcePosition createDeclarationSourcePosition( + CompilationUnit compilationUnit, + int startSource, int end, + int modifierStart, int modifierEnd, + int declarationStart, int declarationEnd, + int[] lineSeparatorPositions); + + /** + * Creates a body holder source position. + */ + BodyHolderSourcePosition createBodyHolderSourcePosition( + CompilationUnit compilationUnit, + int startSource, int end, + int modifierStart, int modifierEnd, + int declarationStart, int declarationEnd, + int bodyStart, int bodyEnd, int[] lineSeparatorPositions); /** * Creates a statement list. diff --git a/src/main/java/spoon/support/DefaultCoreFactory.java b/src/main/java/spoon/support/DefaultCoreFactory.java index df717ea812f..2339aa7ccf1 100644 --- a/src/main/java/spoon/support/DefaultCoreFactory.java +++ b/src/main/java/spoon/support/DefaultCoreFactory.java @@ -64,6 +64,8 @@ import spoon.reflect.code.CtWhile; import spoon.reflect.cu.CompilationUnit; import spoon.reflect.cu.SourcePosition; +import spoon.reflect.cu.position.BodyHolderSourcePosition; +import spoon.reflect.cu.position.DeclarationSourcePosition; import spoon.reflect.declaration.CtAnnotation; import spoon.reflect.declaration.CtAnnotationMethod; import spoon.reflect.declaration.CtAnnotationType; @@ -140,7 +142,9 @@ import spoon.support.reflect.code.CtVariableWriteImpl; import spoon.support.reflect.code.CtWhileImpl; import spoon.support.reflect.cu.CompilationUnitImpl; -import spoon.support.reflect.cu.SourcePositionImpl; +import spoon.support.reflect.cu.position.BodyHolderSourcePositionImpl; +import spoon.support.reflect.cu.position.DeclarationSourcePositionImpl; +import spoon.support.reflect.cu.position.SourcePositionImpl; import spoon.support.reflect.declaration.CtAnnotationImpl; import spoon.support.reflect.declaration.CtAnnotationMethodImpl; import spoon.support.reflect.declaration.CtAnnotationTypeImpl; @@ -637,8 +641,21 @@ public void setMainFactory(Factory mainFactory) { this.factory = mainFactory; } - public SourcePosition createSourcePosition(CompilationUnit compilationUnit, int startDeclaration, int startSource, int end, int[] lineSeparatorPositions) { - return new SourcePositionImpl(compilationUnit, startDeclaration, startSource, end, lineSeparatorPositions); + public SourcePosition createSourcePosition(CompilationUnit compilationUnit, int startSource, int end, int[] lineSeparatorPositions) { + return new SourcePositionImpl(compilationUnit, startSource, end, lineSeparatorPositions); + } + + public DeclarationSourcePosition createDeclarationSourcePosition(CompilationUnit compilationUnit, int startSource, int end, int modifierStart, int modifierEnd, int declarationStart, int declarationEnd, int[] lineSeparatorPositions) { + return new DeclarationSourcePositionImpl(compilationUnit, startSource, end, modifierStart, modifierEnd, declarationStart, declarationEnd, lineSeparatorPositions); + } + + public BodyHolderSourcePosition createBodyHolderSourcePosition(CompilationUnit compilationUnit, int startSource, int end, int modifierStart, int modifierEnd, int declarationStart, int declarationEnd, int bodyStart, int bodyEnd, int[] lineSeparatorPositions) { + return new BodyHolderSourcePositionImpl(compilationUnit, + startSource, end, + modifierStart, modifierEnd, + declarationStart, declarationEnd, + bodyStart, bodyEnd, + lineSeparatorPositions); } public CompilationUnit createCompilationUnit() { diff --git a/src/main/java/spoon/support/compiler/jdt/JDTCommentBuilder.java b/src/main/java/spoon/support/compiler/jdt/JDTCommentBuilder.java index 3e34aee37b8..3037c93293a 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTCommentBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTCommentBuilder.java @@ -123,7 +123,7 @@ private void buildComment(int[] positions) { String commentContent = getCommentContent(start, end); int[] lineSeparatorPositions = declarationUnit.compilationResult.lineSeparatorPositions; - SourcePosition sourcePosition = factory.Core().createSourcePosition(spoonUnit, start, start, end, lineSeparatorPositions); + SourcePosition sourcePosition = factory.Core().createSourcePosition(spoonUnit, start, end, lineSeparatorPositions); // create the Spoon comment element comment.setContent(commentContent); diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java index 399f9a62d0b..02be2fb67c6 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java @@ -43,7 +43,6 @@ import spoon.reflect.code.CtLambda; import spoon.reflect.code.CtTypeAccess; import spoon.reflect.code.CtVariableAccess; -import spoon.reflect.cu.CompilationUnit; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtField; import spoon.reflect.declaration.CtMethod; @@ -285,6 +284,8 @@ CtVariableAccess createVariableAccess(QualifiedNameReference qualifiedNam va = createVariableAccess(ref, isOtherBinding && fromAssignment); } + ref.setPosition(jdtTreeBuilder.getPositionBuilder().buildPosition(sourceStart, sourceEnd)); + if (qualifiedNameReference.otherBindings != null) { int i = 0; //positions index; va.setPosition(ref.getPosition()); @@ -310,10 +311,8 @@ CtVariableAccess createVariableAccess(QualifiedNameReference qualifiedNam CtFieldAccess other = createFieldAccess(// jdtTreeBuilder.getReferencesBuilder().getVariableReference(null, qualifiedNameReference.tokens[i]), va, isOtherBinding && fromAssignment); //set source position of va; - CompilationUnit cu = jdtTreeBuilder.getFactory().CompilationUnit().create(new String(jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.getFileName())); sourceEnd = (int) (positions[i]); - final int[] lineSeparatorPositions = jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.compilationResult.lineSeparatorPositions; - va.setPosition(jdtTreeBuilder.getFactory().Core().createSourcePosition(cu, sourceStart, sourceStart, sourceEnd, lineSeparatorPositions)); + va.setPosition(jdtTreeBuilder.getPositionBuilder().buildPosition(sourceStart, sourceEnd)); va = other; } } diff --git a/src/main/java/spoon/support/compiler/jdt/PositionBuilder.java b/src/main/java/spoon/support/compiler/jdt/PositionBuilder.java index d88d39d3f30..ca84c9b0c91 100644 --- a/src/main/java/spoon/support/compiler/jdt/PositionBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/PositionBuilder.java @@ -19,16 +19,21 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.Javadoc; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import spoon.reflect.code.CtStatementList; import spoon.reflect.cu.CompilationUnit; import spoon.reflect.cu.SourcePosition; import spoon.reflect.declaration.CtElement; -import spoon.reflect.declaration.CtNamedElement; import spoon.reflect.factory.CoreFactory; +import static spoon.support.compiler.jdt.JDTTreeBuilderQuery.getModifiers; + /** * Created by bdanglot on 07/07/16. */ @@ -43,17 +48,15 @@ public PositionBuilder(JDTTreeBuilder jdtTreeBuilder) { SourcePosition buildPosition(int sourceStart, int sourceEnd) { CompilationUnit cu = this.jdtTreeBuilder.getContextBuilder().compilationUnitSpoon; final int[] lineSeparatorPositions = this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.compilationResult.lineSeparatorPositions; - return this.jdtTreeBuilder.getFactory().Core().createSourcePosition(cu, sourceStart, sourceStart, sourceEnd, lineSeparatorPositions); + return this.jdtTreeBuilder.getFactory().Core().createSourcePosition(cu, sourceStart, sourceEnd, lineSeparatorPositions); } SourcePosition buildPositionCtElement(CtElement e, ASTNode node) { CoreFactory cf = this.jdtTreeBuilder.getFactory().Core(); - int sourceStartDeclaration = node.sourceStart; - int sourceStartSource = node.sourceStart; - // default value - if (!(e instanceof CtNamedElement)) { - sourceStartSource = sourceStartDeclaration; - } + CompilationUnit cu = this.jdtTreeBuilder.getFactory().CompilationUnit().create(new String(this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.getFileName())); + int[] lineSeparatorPositions = this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.compilationResult.lineSeparatorPositions; + + int sourceStart = node.sourceStart; int sourceEnd = node.sourceEnd; if ((node instanceof Expression)) { if (((Expression) node).statementEnd > 0) { @@ -62,25 +65,129 @@ SourcePosition buildPositionCtElement(CtElement e, ASTNode node) { } if (node instanceof AbstractVariableDeclaration) { - sourceStartDeclaration = ((AbstractVariableDeclaration) node).declarationSourceStart; - sourceEnd = ((AbstractVariableDeclaration) node).declarationSourceEnd; + AbstractVariableDeclaration variableDeclaration = (AbstractVariableDeclaration) node; + int modifiersSourceStart = variableDeclaration.modifiersSourceStart; + int declarationSourceStart = variableDeclaration.declarationSourceStart; + int declarationSourceEnd = variableDeclaration.declarationSourceEnd; + int declarationEnd = variableDeclaration.declarationEnd; + + Annotation[] annotations = variableDeclaration.annotations; + if (annotations != null && annotations.length > 0) { + if (annotations[0].sourceStart() == sourceStart) { + modifiersSourceStart = annotations[annotations.length - 1].sourceEnd() + 2; + } + } + if (modifiersSourceStart == 0) { + modifiersSourceStart = declarationSourceStart; + } + int modifiersSourceEnd = 0; + if (variableDeclaration.type != null) { + modifiersSourceEnd = variableDeclaration.type.sourceStart() - 2; + } else { + // variable that has no type such as TypeParameter + modifiersSourceEnd = declarationSourceStart - 1; + } + + // when no modifier + if (modifiersSourceStart > modifiersSourceEnd) { + modifiersSourceEnd = modifiersSourceStart - 1; + } + + return cf.createDeclarationSourcePosition(cu, + sourceStart, sourceEnd, + modifiersSourceStart, modifiersSourceEnd, + declarationSourceStart, declarationSourceEnd, + lineSeparatorPositions); } else if (node instanceof TypeDeclaration) { - sourceStartDeclaration = ((TypeDeclaration) node).declarationSourceStart; - sourceEnd = ((TypeDeclaration) node).declarationSourceEnd; - } else if ((e instanceof CtStatementList) && (node instanceof AbstractMethodDeclaration)) { - sourceStartDeclaration = ((AbstractMethodDeclaration) node).bodyStart - 1; - sourceEnd = ((AbstractMethodDeclaration) node).bodyEnd + 1; - } else if ((node instanceof AbstractMethodDeclaration)) { - if (((AbstractMethodDeclaration) node).bodyStart == 0) { - sourceStartDeclaration = -1; - sourceStartSource = -1; - sourceEnd = -1; + TypeDeclaration typeDeclaration = (TypeDeclaration) node; + + int declarationSourceStart = typeDeclaration.declarationSourceStart; + int declarationSourceEnd = typeDeclaration.declarationSourceEnd; + int modifiersSourceStart = typeDeclaration.modifiersSourceStart; + int bodyStart = typeDeclaration.bodyStart; + int bodyEnd = typeDeclaration.bodyEnd; + + Annotation[] annotations = typeDeclaration.annotations; + if (annotations != null && annotations.length > 0) { + if (annotations[0].sourceStart() == declarationSourceStart) { + modifiersSourceStart = annotations[annotations.length - 1].sourceEnd() + 2; + } + } + if (modifiersSourceStart == 0) { + modifiersSourceStart = declarationSourceStart; + } + // the position the name minus the size of "class" minus at least 2 spaces + int modifiersSourceEnd = sourceStart - 8; + + return cf.createBodyHolderSourcePosition(cu, sourceStart, sourceEnd, + modifiersSourceStart, modifiersSourceEnd, + declarationSourceStart, declarationSourceEnd, + bodyStart - 1, bodyEnd, + lineSeparatorPositions); + } else if (node instanceof AbstractMethodDeclaration) { + AbstractMethodDeclaration methodDeclaration = (AbstractMethodDeclaration) node; + int bodyStart = methodDeclaration.bodyStart; + int bodyEnd = methodDeclaration.bodyEnd; + int declarationSourceStart = methodDeclaration.declarationSourceStart; + int declarationSourceEnd = methodDeclaration.declarationSourceEnd; + int modifiersSourceStart = methodDeclaration.modifiersSourceStart; + + if (modifiersSourceStart == 0) { + modifiersSourceStart = declarationSourceStart; + } + Javadoc javadoc = methodDeclaration.javadoc; + if (javadoc != null && javadoc.sourceEnd() > declarationSourceStart) { + modifiersSourceStart = javadoc.sourceEnd() + 1; + } + Annotation[] annotations = methodDeclaration.annotations; + if (annotations != null && annotations.length > 0) { + if (annotations[0].sourceStart() == declarationSourceStart) { + modifiersSourceStart = annotations[annotations.length - 1].sourceEnd() + 2; + } + } + + int modifiersSourceEnd = sourceStart - 1; + + if (methodDeclaration instanceof MethodDeclaration && ((MethodDeclaration) methodDeclaration).returnType != null) { + modifiersSourceEnd = ((MethodDeclaration) methodDeclaration).returnType.sourceStart() - 2; + } + + TypeParameter[] typeParameters = methodDeclaration.typeParameters(); + if (typeParameters != null && typeParameters.length > 0) { + modifiersSourceEnd = typeParameters[0].declarationSourceStart - 3; + } + + if (getModifiers(methodDeclaration.modifiers).isEmpty()) { + modifiersSourceStart = modifiersSourceEnd + 1; + } + + + sourceEnd = sourceStart + methodDeclaration.selector.length - 1; + + if (e instanceof CtStatementList) { + return cf.createSourcePosition(cu, bodyStart - 1, bodyEnd + 1, lineSeparatorPositions); } else { - sourceStartDeclaration = ((AbstractMethodDeclaration) node).declarationSourceStart; - sourceEnd = ((AbstractMethodDeclaration) node).declarationSourceEnd; + if (bodyStart == 0) { + return SourcePosition.NOPOSITION; + } else { + return cf.createBodyHolderSourcePosition(cu, + sourceStart, sourceEnd, + modifiersSourceStart, modifiersSourceEnd, + declarationSourceStart, declarationSourceEnd, + bodyStart - 1, bodyEnd + 1, + lineSeparatorPositions); + } } } - CompilationUnit cu = this.jdtTreeBuilder.getContextBuilder().compilationUnitSpoon; - return cf.createSourcePosition(cu, sourceStartDeclaration, sourceStartSource, sourceEnd, this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.compilationResult.lineSeparatorPositions); + if ((node instanceof Expression)) { + Expression expression = (Expression) node; + int statementEnd = expression.statementEnd; + + if (statementEnd > 0) { + sourceEnd = statementEnd; + } + } + + return cf.createSourcePosition(cu, sourceStart, sourceEnd, lineSeparatorPositions); } } diff --git a/src/main/java/spoon/support/reflect/cu/position/BodyHolderSourcePositionImpl.java b/src/main/java/spoon/support/reflect/cu/position/BodyHolderSourcePositionImpl.java new file mode 100644 index 00000000000..1d5099a7eda --- /dev/null +++ b/src/main/java/spoon/support/reflect/cu/position/BodyHolderSourcePositionImpl.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2006-2016 INRIA and contributors + * Spoon - http://spoon.gforge.inria.fr/ + * + * This software is governed by the CeCILL-C License under French law and + * abiding by the rules of distribution of free software. You can use, modify + * and/or redistribute the software under the terms of the CeCILL-C license as + * circulated by CEA, CNRS and INRIA at http://www.cecill.info. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package spoon.support.reflect.cu.position; + +import spoon.reflect.cu.CompilationUnit; +import spoon.reflect.cu.position.BodyHolderSourcePosition; + +import java.io.Serializable; + +/** + * This class represents the position of a Java program element in a source + * file. + */ +public class BodyHolderSourcePositionImpl extends DeclarationSourcePositionImpl + implements BodyHolderSourcePosition, Serializable { + + private static final long serialVersionUID = 1L; + private int bodyStart; + private int bodyEnd; + + public BodyHolderSourcePositionImpl( + CompilationUnit compilationUnit, + int sourceStart, int sourceEnd, + int modifierSourceStart, int modifierSourceEnd, + int declarationSourceStart, int declarationSourceEnd, + int bodyStart, + int bodyEnd, + int[] lineSeparatorPositions) { + super(compilationUnit, + sourceStart, sourceEnd, + modifierSourceStart, modifierSourceEnd, + declarationSourceStart, declarationSourceEnd, + lineSeparatorPositions); + this.bodyStart = bodyStart; + this.bodyEnd = bodyEnd; + } + + @Override + public int getBodyStart() { + return bodyStart; + } + + @Override + public int getBodyEnd() { + return bodyEnd; + } +} diff --git a/src/main/java/spoon/support/reflect/cu/position/DeclarationSourcePositionImpl.java b/src/main/java/spoon/support/reflect/cu/position/DeclarationSourcePositionImpl.java new file mode 100644 index 00000000000..0b3c455c38d --- /dev/null +++ b/src/main/java/spoon/support/reflect/cu/position/DeclarationSourcePositionImpl.java @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2006-2016 INRIA and contributors + * Spoon - http://spoon.gforge.inria.fr/ + * + * This software is governed by the CeCILL-C License under French law and + * abiding by the rules of distribution of free software. You can use, modify + * and/or redistribute the software under the terms of the CeCILL-C license as + * circulated by CEA, CNRS and INRIA at http://www.cecill.info. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package spoon.support.reflect.cu.position; + +import spoon.reflect.cu.CompilationUnit; +import spoon.reflect.cu.position.DeclarationSourcePosition; + +import java.io.Serializable; + +/** + * This class represents the position of a Java program element in a source + * file. + */ +public class DeclarationSourcePositionImpl extends SourcePositionImpl + implements DeclarationSourcePosition, Serializable { + + private static final long serialVersionUID = 1L; + private int modifierSourceEnd; + private int modifierSourceStart; + private int declarationSourceStart; + private int declarationSourceEnd; + + public DeclarationSourcePositionImpl(CompilationUnit compilationUnit, int sourceStart, int sourceEnd, + int modifierSourceStart, int modifierSourceEnd, int declarationSourceStart, int declarationSourceEnd, + int[] lineSeparatorPositions) { + super(compilationUnit, + sourceStart, sourceEnd, + lineSeparatorPositions); + this.modifierSourceStart = modifierSourceStart; + this.declarationSourceStart = declarationSourceStart; + this.declarationSourceEnd = declarationSourceEnd; + if (this.modifierSourceStart == 0) { + this.modifierSourceStart = declarationSourceStart; + } + this.modifierSourceEnd = modifierSourceEnd; + } + + @Override + public int getSourceEnd() { + return declarationSourceEnd; + } + + @Override + public int getSourceStart() { + return declarationSourceStart; + } + + @Override + public int getModifierSourceStart() { + return modifierSourceStart; + } + + @Override + public int getNameStart() { + return super.getSourceStart(); + } + + @Override + public int getNameEnd() { + return super.getSourceEnd(); + } + + public void setModifierSourceEnd(int modifierSourceEnd) { + this.modifierSourceEnd = modifierSourceEnd; + } + + @Override + public int getModifierSourceEnd() { + return modifierSourceEnd; + } + + public int getEndLine() { + return searchLineNumber(declarationSourceEnd); + } +} diff --git a/src/main/java/spoon/support/reflect/cu/SourcePositionImpl.java b/src/main/java/spoon/support/reflect/cu/position/SourcePositionImpl.java similarity index 90% rename from src/main/java/spoon/support/reflect/cu/SourcePositionImpl.java rename to src/main/java/spoon/support/reflect/cu/position/SourcePositionImpl.java index 38ebd81c866..6b834c52c76 100644 --- a/src/main/java/spoon/support/reflect/cu/SourcePositionImpl.java +++ b/src/main/java/spoon/support/reflect/cu/position/SourcePositionImpl.java @@ -14,7 +14,7 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. */ -package spoon.support.reflect.cu; +package spoon.support.reflect.cu.position; import spoon.reflect.cu.CompilationUnit; import spoon.reflect.cu.SourcePosition; @@ -33,9 +33,9 @@ public class SourcePositionImpl implements SourcePosition, Serializable { /** * Search the line number corresponding to a specific position */ - private int searchLineNumber(int position) { + protected int searchLineNumber(int position) { if (lineSeparatorPositions == null) { - return -1; + return 1; } int length = lineSeparatorPositions.length; if (length == 0) { @@ -96,9 +96,6 @@ private int searchColumnNumber(int position) { /** The position of the last byte of this element */ private int sourceEnd = -1; - /** The position of the first byte of the name of the element, if appropriate (eg the method name) */ - private int nameSourceStart = 1; - /** The line number of the start of the element, if appropriate (eg the method name). * Computed lazily by {@link #getLine()} */ @@ -110,17 +107,16 @@ private int searchColumnNumber(int position) { */ int[] lineSeparatorPositions; - public SourcePositionImpl(CompilationUnit compilationUnit, int sourceStart, int nameSourceStart, int sourceEnd, int[] lineSeparatorPositions) { + public SourcePositionImpl(CompilationUnit compilationUnit, int sourceStart, int sourceEnd, int[] lineSeparatorPositions) { super(); this.compilationUnit = compilationUnit; - this.nameSourceStart = nameSourceStart; this.sourceEnd = sourceEnd; this.sourceStart = sourceStart; this.lineSeparatorPositions = lineSeparatorPositions; } public int getColumn() { - return searchColumnNumber(nameSourceStart); + return searchColumnNumber(sourceStart); } public int getEndColumn() { @@ -136,7 +132,7 @@ public File getFile() { public int getLine() { if (sourceStartline == -1) { - this.sourceStartline = searchLineNumber(this.nameSourceStart); + this.sourceStartline = searchLineNumber(this.sourceStart); } return sourceStartline; } @@ -191,9 +187,4 @@ public CompilationUnit getCompilationUnit() { return compilationUnit; } - @Override - public int getNameSourceStart() { - return nameSourceStart; - } - } diff --git a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java index 3c0062353f3..a4d2163fa8c 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java @@ -33,7 +33,6 @@ import spoon.reflect.visitor.Query; import spoon.reflect.visitor.ReferenceFilter; import spoon.reflect.visitor.filter.AnnotationFilter; -import spoon.support.reflect.cu.SourcePositionImpl; import spoon.support.util.EmptyClearableList; import spoon.support.util.EmptyClearableSet; import spoon.support.visitor.HashcodeVisitor; @@ -83,9 +82,7 @@ public static List unmodifiableList(List list) { private List comments = emptyList(); - public final SourcePosition DEFAULT_POSITION = new SourcePositionImpl(null, -1, -1, -1, null); - - SourcePosition position = DEFAULT_POSITION; + SourcePosition position = SourcePosition.NOPOSITION; Map metadata; diff --git a/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java b/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java index 4387351f0d5..bc551f0aa66 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtPackageImpl.java @@ -17,7 +17,6 @@ package spoon.support.reflect.declaration; import java.util.Set; - import spoon.reflect.cu.SourcePosition; import spoon.reflect.declaration.CtPackage; import spoon.reflect.declaration.CtShadowable; diff --git a/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java b/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java index dec319e874c..5208032c5cc 100644 --- a/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java +++ b/src/main/java/spoon/support/reflect/declaration/CtTypeImpl.java @@ -18,7 +18,6 @@ import spoon.SpoonException; import spoon.reflect.code.CtBlock; -import spoon.reflect.cu.CompilationUnit; import spoon.reflect.declaration.CtAnnotation; import spoon.reflect.declaration.CtAnnotationType; import spoon.reflect.declaration.CtClass; @@ -137,13 +136,6 @@ public > C setTypeMembers(List members) { @Override public > C addFieldAtTop(CtField field) { - if (field != null && !this.typeMembers.contains(field)) { - CompilationUnit compilationUnit = null; - if (getPosition() != null) { - compilationUnit = getPosition().getCompilationUnit(); - } - field.setPosition(getFactory().Core().createSourcePosition(compilationUnit, -1, -1, -1, new int[0])); - } return addTypeMemberAt(0, field); } diff --git a/src/test/java/spoon/test/position/PositionTest.java b/src/test/java/spoon/test/position/PositionTest.java index 8a9148108d5..2554d60a597 100644 --- a/src/test/java/spoon/test/position/PositionTest.java +++ b/src/test/java/spoon/test/position/PositionTest.java @@ -1,33 +1,37 @@ package spoon.test.position; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static spoon.testing.utils.ModelUtils.build; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Paths; - import org.junit.Test; - import spoon.reflect.code.CtAssignment; import spoon.reflect.code.CtBlock; import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtFieldAccess; import spoon.reflect.code.CtIf; import spoon.reflect.cu.SourcePosition; +import spoon.reflect.cu.position.BodyHolderSourcePosition; +import spoon.reflect.cu.position.DeclarationSourcePosition; +import spoon.reflect.declaration.CtClass; +import spoon.reflect.declaration.CtConstructor; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtType; import spoon.reflect.factory.Factory; import spoon.test.position.testclasses.FooClazz; import spoon.test.position.testclasses.FooClazz2; import spoon.test.position.testclasses.FooField; +import spoon.test.position.testclasses.FooGeneric; import spoon.test.position.testclasses.FooMethod; import spoon.test.position.testclasses.FooStatement; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static spoon.testing.utils.ModelUtils.build; + public class PositionTest { @Test @@ -36,27 +40,28 @@ public void testPositionClass() throws Exception { final CtType foo = build.Type().get(FooClazz.class); String classContent = getClassContent(foo); - SourcePosition position = foo.getPosition(); + BodyHolderSourcePosition position = (BodyHolderSourcePosition) foo.getPosition(); assertEquals(4, position.getLine()); assertEquals(6, position.getEndLine()); assertEquals(42, position.getSourceStart()); - assertEquals(67, position.getNameSourceStart()); assertEquals(79, position.getSourceEnd()); assertEquals("@Deprecated\n" + "public class FooClazz {\n" + "\n" + "}", contentAtPosition(classContent, position)); + assertEquals("{\n\n}", contentAtPosition(classContent, position.getBodyStart(), position.getBodyEnd())); + // this specifies that getLine starts at name (and not at Javadoc or annotation) final CtType foo2 = build.Type().get(FooClazz2.class); assertEquals(42, foo2.getPosition().getSourceStart()); - assertEquals(98, foo2.getPosition().getNameSourceStart()); assertEquals(4, foo2.getPosition().getLine()); assertEquals(4, foo2.getPosition().getEndLine()); - + assertEquals("FooClazz", contentAtPosition(classContent, position.getNameStart(), position.getNameEnd())); + assertEquals("public", contentAtPosition(classContent, position.getModifierSourceStart(), position.getModifierSourceEnd())); } @Test @@ -65,7 +70,7 @@ public void testPositionField() throws Exception { final CtType foo = build.Type().get(FooField.class); String classContent = getClassContent(foo); - SourcePosition position1 = foo.getField("field1").getPosition(); + DeclarationSourcePosition position1 = (DeclarationSourcePosition) foo.getField("field1").getPosition(); assertEquals(5, position1.getLine()); assertEquals(5, position1.getEndLine()); @@ -74,8 +79,10 @@ public void testPositionField() throws Exception { assertEquals(95, position1.getSourceEnd()); assertEquals("public final int field1 = 0;", contentAtPosition(classContent, position1)); + assertEquals("field1", contentAtPosition(classContent, position1.getNameStart(), position1.getNameEnd())); + assertEquals("public final", contentAtPosition(classContent, position1.getModifierSourceStart(), position1.getModifierSourceEnd())); - SourcePosition position2 = foo.getField("field2").getPosition(); + DeclarationSourcePosition position2 = (DeclarationSourcePosition) foo.getField("field2").getPosition(); assertEquals(7, position2.getLine()); assertEquals(8, position2.getEndLine()); @@ -85,7 +92,8 @@ public void testPositionField() throws Exception { assertEquals("int field2 =\n" + "\t\t\t0;", contentAtPosition(classContent, position2)); - + assertEquals("field2", contentAtPosition(classContent, position2.getNameStart(), position2.getNameEnd())); + assertEquals("", contentAtPosition(classContent, position2.getModifierSourceStart(), position2.getModifierSourceEnd())); CtAssignment m = foo.getMethod("m").getBody().getStatement(0); CtFieldAccess assigned = (CtFieldAccess) m.getAssigned(); @@ -119,14 +127,57 @@ public void testPositionField() throws Exception { assertEquals("FooField", contentAtPosition(classContent, position5)); } + @Test + public void testPositionGeneric() throws Exception { + final Factory build = build(FooGeneric.class); + final CtClass foo = build.Class().get(FooGeneric.class); + String classContent = getClassContent(foo); + + BodyHolderSourcePosition position = (BodyHolderSourcePosition) foo.getPosition(); + + assertEquals(3, position.getLine()); + assertEquals(31, position.getEndLine()); + + assertEquals(42, position.getSourceStart()); + assertEquals(411, position.getSourceEnd()); + + assertEquals("FooGeneric", contentAtPosition(classContent, position.getNameStart(), position.getNameEnd())); + assertEquals("public", contentAtPosition(classContent, position.getModifierSourceStart(), position.getModifierSourceEnd())); + + + DeclarationSourcePosition position1 = (DeclarationSourcePosition) foo.getField("variable").getPosition(); + + assertEquals(5, position1.getLine()); + assertEquals(5, position1.getEndLine()); + + assertEquals(88, position1.getSourceStart()); + assertEquals(118, position1.getSourceEnd()); + + assertEquals("public final T variable = null;", contentAtPosition(classContent, position1)); + assertEquals("variable", contentAtPosition(classContent, position1.getNameStart(), position1.getNameEnd())); + assertEquals("public final", contentAtPosition(classContent, position1.getModifierSourceStart(), position1.getModifierSourceEnd())); + + CtMethod method1 = foo.getMethodsByName("m").get(0); + BodyHolderSourcePosition position2 = (BodyHolderSourcePosition) method1 + .getPosition(); + + assertEquals("public @Deprecated static S m(int parm1) {\n" + + "\t\treturn null;\n" + + "\t}", contentAtPosition(classContent, position2)); + assertEquals("m", contentAtPosition(classContent, position2.getNameStart(), position2.getNameEnd())); + + // /!\ the annotations can be between two modifiers + assertEquals("public @Deprecated static", contentAtPosition(classContent, position2.getModifierSourceStart(), position2.getModifierSourceEnd())); + } + @Test public void testPositionMethod() throws Exception { final Factory build = build(FooMethod.class); - final CtType foo = build.Type().get(FooMethod.class); + final CtClass foo = build.Class().get(FooMethod.class); String classContent = getClassContent(foo); CtMethod method1 = foo.getMethodsByName("m").get(0); - SourcePosition position1 = method1.getPosition(); + BodyHolderSourcePosition position1 = (BodyHolderSourcePosition) method1.getPosition(); assertEquals(5, position1.getLine()); assertEquals(7, position1.getEndLine()); @@ -137,8 +188,10 @@ public void testPositionMethod() throws Exception { assertEquals("public static void m(int parm1) {\n" + "\t\treturn;\n" + "\t}", contentAtPosition(classContent, position1)); + assertEquals("m", contentAtPosition(classContent, position1.getNameStart(), position1.getNameEnd())); + assertEquals("public static", contentAtPosition(classContent, position1.getModifierSourceStart(), position1.getModifierSourceEnd())); - SourcePosition positionParam1 = method1.getParameters().get(0).getPosition(); + DeclarationSourcePosition positionParam1 = (DeclarationSourcePosition) method1.getParameters().get(0).getPosition(); assertEquals(5, positionParam1.getLine()); assertEquals(5, positionParam1.getEndLine()); @@ -147,10 +200,11 @@ public void testPositionMethod() throws Exception { assertEquals(98, positionParam1.getSourceEnd()); assertEquals("int parm1", contentAtPosition(classContent, positionParam1)); + assertEquals("parm1", contentAtPosition(classContent, positionParam1.getNameStart(), positionParam1.getNameEnd())); + assertEquals("", contentAtPosition(classContent, positionParam1.getModifierSourceStart(), positionParam1.getModifierSourceEnd())); - - CtMethod method2 = foo.getMethodsByName("mWithDoc").get(0); - SourcePosition position2 = method2.getPosition(); + CtMethod method2 = foo.getMethodsByName("mWithDoc").get(0); + BodyHolderSourcePosition position2 = (BodyHolderSourcePosition) method2.getPosition(); assertEquals(13, position2.getLine()); assertEquals(15, position2.getEndLine()); @@ -162,6 +216,16 @@ public void testPositionMethod() throws Exception { + "\tint mWithDoc(int parm1) {\n" + "\t\treturn parm1;\n" + "\t}", contentAtPosition(classContent, position2)); + assertEquals("mWithDoc", contentAtPosition(classContent, position2.getNameStart(), position2.getNameEnd())); + assertEquals("", contentAtPosition(classContent, position2.getModifierSourceStart(), position2.getModifierSourceEnd())); + + CtConstructor constructor = foo.getConstructor(build.Type().integerPrimitiveType()); + SourcePosition position3 = constructor.getPosition(); + contentAtPosition(classContent, position3); + + CtMethod mWithLine = foo.getMethod("mWithLine", build.Type().integerPrimitiveType()); + SourcePosition position4 = mWithLine.getPosition(); + contentAtPosition(classContent, position4); } @Test @@ -282,6 +346,10 @@ private String getClassContent(CtType type) { return content; } + private String contentAtPosition(String content, int start, int end) { + return content.substring(start, end + 1); + } + private String contentAtPosition(String content, SourcePosition position) { return content.substring(position.getSourceStart(), position.getSourceEnd() + 1); } diff --git a/src/test/java/spoon/test/position/testclasses/FooGeneric.java b/src/test/java/spoon/test/position/testclasses/FooGeneric.java new file mode 100644 index 00000000000..21874e7c38d --- /dev/null +++ b/src/test/java/spoon/test/position/testclasses/FooGeneric.java @@ -0,0 +1,31 @@ +package spoon.test.position.testclasses; + +public class FooGeneric { + + public final T variable = null; + + public @Deprecated static S m(int parm1) { + return null; + } + + /** + * Mathod with javadoc + * @param parm1 the parameter + */ + int mWithDoc(int parm1) { + return parm1; + } + + + public + static + final + int mWithLine + (int parm1) { + return parm1; + } + + public FooGeneric(int arg1) { + + } +} \ No newline at end of file diff --git a/src/test/java/spoon/test/position/testclasses/FooMethod.java b/src/test/java/spoon/test/position/testclasses/FooMethod.java index c627029ee26..161e0103ba3 100644 --- a/src/test/java/spoon/test/position/testclasses/FooMethod.java +++ b/src/test/java/spoon/test/position/testclasses/FooMethod.java @@ -13,4 +13,17 @@ public static void m(int parm1) { int mWithDoc(int parm1) { return parm1; } + + + public + static + final + int mWithLine + (int parm1) { + return parm1; + } + + public FooMethod(int arg1) { + + } } \ No newline at end of file