From 96f7b13812ac070074637abcb9582c1bd61d4361 Mon Sep 17 00:00:00 2001
From: Pavel Vojtechovsky
Date: Wed, 11 Apr 2018 22:01:49 +0200
Subject: [PATCH] javadoc and some cleaning
---
.../ChangesAwareDefaultJavaPrettyPrinter.java | 172 ++++++++++--------
.../printer/change/DirectPrinterHelper.java | 15 +-
.../visitor/printer/change/FragmentType.java | 2 +-
.../printer/change/MutableTokenWriter.java | 21 ++-
.../visitor/printer/change/PrintAction.java | 24 ---
.../printer/change/SourceFragment.java | 122 ++++++++++---
.../printer/change/SourceFragmentContext.java | 3 +
.../change/SourceFragmentContextList.java | 91 ++++-----
.../change/SourceFragmentContextNormal.java | 18 +-
.../reflect/cu/CompilationUnitImpl.java | 4 +-
.../test/position/SourceFragmentTest.java | 8 +-
11 files changed, 286 insertions(+), 194 deletions(-)
delete mode 100644 src/main/java/spoon/reflect/visitor/printer/change/PrintAction.java
diff --git a/src/main/java/spoon/reflect/visitor/printer/change/ChangesAwareDefaultJavaPrettyPrinter.java b/src/main/java/spoon/reflect/visitor/printer/change/ChangesAwareDefaultJavaPrettyPrinter.java
index 34b2af9c258..2e5041a3b44 100644
--- a/src/main/java/spoon/reflect/visitor/printer/change/ChangesAwareDefaultJavaPrettyPrinter.java
+++ b/src/main/java/spoon/reflect/visitor/printer/change/ChangesAwareDefaultJavaPrettyPrinter.java
@@ -16,10 +16,6 @@
*/
package spoon.reflect.visitor.printer.change;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
import java.util.ArrayDeque;
import java.util.Deque;
@@ -30,17 +26,23 @@
import spoon.reflect.declaration.CtElement;
import spoon.reflect.path.CtRole;
import spoon.reflect.visitor.DefaultJavaPrettyPrinter;
+import spoon.reflect.visitor.PrettyPrinter;
+import spoon.reflect.visitor.PrinterHelper;
import spoon.reflect.visitor.TokenWriter;
/**
- * SourcePositionUtils#descriptors
+ * {@link PrettyPrinter} implementation which copies as much as possible from origin sources
+ * and prints only changed elements
*/
public class ChangesAwareDefaultJavaPrettyPrinter extends DefaultJavaPrettyPrinter {
- final MutableTokenWriter mutableTokenWriter;
+ private final MutableTokenWriter mutableTokenWriter;
private final ChangeCollector changeCollector;
private final Deque sourceFragmentContextStack = new ArrayDeque<>();
+ /**
+ * Creates a new {@link PrettyPrinter} which copies origin sources and prints only changes.
+ */
public ChangesAwareDefaultJavaPrettyPrinter(Environment env) {
super(env);
this.changeCollector = ChangeCollector.getChangeCollector(env);
@@ -60,70 +62,89 @@ public ChangesAwareDefaultJavaPrettyPrinter(Environment env) {
* @return a wrapped {@link TokenWriter}
*/
private TokenWriter createTokenWriterListener(TokenWriter tokenWriter) {
- return (TokenWriter) Proxy.newProxyInstance(
- getClass().getClassLoader(),
- new Class[] {TokenWriter.class},
- new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Runnable printAction = () -> {
- try {
- method.invoke(tokenWriter, args);
- } catch (IllegalAccessException | IllegalArgumentException e) {
- throw new SpoonException("Cannot invoke TokenWriter method", e);
- } catch (InvocationTargetException e) {
- if (e.getTargetException() instanceof RuntimeException) {
- throw (RuntimeException) e.getTargetException();
- }
- throw new SpoonException("Invokation target exception TokenWriter method", e);
- }
- };
- if (method.getName().startsWith("write")) {
- Class>[] paramTypes = method.getParameterTypes();
- if (paramTypes.length == 1) {
- if (paramTypes[0] == String.class) {
- //writeXxx(String)
- onTokenWriterWrite(method.getName(), (String) args[0], null, printAction);
- } else if (paramTypes[0] == CtComment.class) {
- //writeComment(CtComment)
- onTokenWriterWrite(method.getName(), null, (CtComment) args[0], printAction);
- }
- return proxy;
- } else if (paramTypes.length == 0) {
- //writeSpace() and writeln()
- String token = method.getName().equals("writeSpace") ? " " : "\n";
- onTokenWriterWrite(method.getName(), token, null, printAction);
- return proxy;
- }
- }
- if (method.getName().endsWith("Tab")) {
- //incTab(), decTab()
- onTokenWriterWrite(method.getName(), (String) null, null, printAction);
- return proxy;
- }
- if (method.getName().equals("getPrinterHelper") || method.getName().equals("reset")) {
- try {
- return method.invoke(tokenWriter, args);
- } catch (IllegalAccessException | IllegalArgumentException e) {
- throw new SpoonException("Cannot invoke TokenWriter method", e);
- } catch (InvocationTargetException e) {
- throw e.getTargetException();
- }
- }
- throw new SpoonException("Unexpected method TokenWriter#" + method.getName());
- }
- });
+ return new TokenWriterProxy(tokenWriter);
+ }
+
+ private class TokenWriterProxy implements TokenWriter {
+ private final TokenWriter delegate;
+
+ TokenWriterProxy(TokenWriter delegate) {
+ super();
+ this.delegate = delegate;
+ }
+
+ public TokenWriter writeSeparator(String token) {
+ onTokenWriterWrite("writeSeparator", token, null, () -> delegate.writeSeparator(token));
+ return this;
+ }
+
+ public TokenWriter writeOperator(String token) {
+ onTokenWriterWrite("writeOperator", token, null, () -> delegate.writeOperator(token));
+ return this;
+ }
+
+ public TokenWriter writeLiteral(String token) {
+ onTokenWriterWrite("writeLiteral", token, null, () -> delegate.writeLiteral(token));
+ return this;
+ }
+
+ public TokenWriter writeKeyword(String token) {
+ onTokenWriterWrite("writeKeyword", token, null, () -> delegate.writeKeyword(token));
+ return this;
+ }
+
+ public TokenWriter writeIdentifier(String token) {
+ onTokenWriterWrite("writeIdentifier", token, null, () -> delegate.writeIdentifier(token));
+ return this;
+ }
+
+ public TokenWriter writeCodeSnippet(String token) {
+ onTokenWriterWrite("writeCodeSnippet", token, null, () -> delegate.writeCodeSnippet(token));
+ return this;
+ }
+
+ public TokenWriter writeComment(CtComment comment) {
+ onTokenWriterWrite("writeComment", null, comment, () -> delegate.writeComment(comment));
+ return this;
+ }
+
+ public TokenWriter writeln() {
+ onTokenWriterWrite("writeln", "\n", null, () -> delegate.writeln());
+ return this;
+ }
+
+ public TokenWriter incTab() {
+ onTokenWriterWrite("incTab", null, null, () -> delegate.incTab());
+ return this;
+ }
+
+ public TokenWriter decTab() {
+ onTokenWriterWrite("decTab", null, null, () -> delegate.decTab());
+ return this;
+ }
+
+ public PrinterHelper getPrinterHelper() {
+ return delegate.getPrinterHelper();
+ }
+
+ public void reset() {
+ delegate.reset();
+ }
+
+ public TokenWriter writeSpace() {
+ onTokenWriterWrite("writeSpace", " ", null, () -> delegate.writeSpace());
+ return this;
+ }
}
/**
* Is called for each printed token
* @param tokenWriterMethodName the name of {@link TokenWriter} method
- * @param token the actual token value
- * @param comment TODO
+ * @param token the actual token value. It may be null for some `tokenWriterMethodName`
+ * @param comment the comment when `tokenWriterMethodName` == `writeComment`
* @param printAction the executor of the action, we are listening for.
- * @throws Exception
*/
- protected void onTokenWriterWrite(String tokenWriterMethodName, String token, CtComment comment, Runnable printAction) {
+ private void onTokenWriterWrite(String tokenWriterMethodName, String token, CtComment comment, Runnable printAction) {
SourceFragmentContext sfc = sourceFragmentContextStack.peek();
if (sfc != null) {
sfc.onTokenWriterToken(tokenWriterMethodName, token, printAction);
@@ -132,14 +153,18 @@ protected void onTokenWriterWrite(String tokenWriterMethodName, String token, Ct
printAction.run();
}
- private final SourceFragmentContextNormal EMPTY_FRAGMENT_CONTEXT = new SourceFragmentContextNormal(this);
+ private final SourceFragmentContextNormal EMPTY_FRAGMENT_CONTEXT = new SourceFragmentContextNormal();
+ /**
+ * Called whenever {@link DefaultJavaPrettyPrinter} scans/prints an element
+ */
@Override
public ChangesAwareDefaultJavaPrettyPrinter scan(CtElement element) {
SourceFragmentContext sfc = sourceFragmentContextStack.peek();
if (sfc != null) {
CtRole role = element.getRoleInParent();
if (role != null) {
+ //there is an context in the child element, let it handle scanning
sfc.onScanElementOnRole(element, role, () -> scanInternal(element));
return this;
}
@@ -161,20 +186,19 @@ private void scanInternal(CtElement element) {
//detect SourceFragments of element and whether they are modified or not
SourceFragment rootFragmentOfElement = SourcePositionUtils.getSourceFragmentsOfElement(changeCollector, element);
if (rootFragmentOfElement == null) {
- //we have no origin sources. Use normal printing
+ //we have no origin sources or this element has one source fragment only and it is modified. Use normal printing
sourceFragmentContextStack.push(EMPTY_FRAGMENT_CONTEXT);
super.scan(element);
sourceFragmentContextStack.pop();
return;
}
- try {
- SourceFragmentContextNormal sfx = new SourceFragmentContextNormal(this, element, rootFragmentOfElement);
- sourceFragmentContextStack.push(sfx);
- super.scan(element);
- } finally {
- //at the end we always un-mute the token writer
- mutableTokenWriter.setMuted(false);
- sourceFragmentContextStack.pop();
- }
+ //the element is modified and it consists of more source fragments, and some of them are not modified
+ //so we can copy the origin sources for them
+ SourceFragmentContextNormal sfx = new SourceFragmentContextNormal(mutableTokenWriter, element, rootFragmentOfElement);
+ sourceFragmentContextStack.push(sfx);
+ super.scan(element);
+ sourceFragmentContextStack.pop();
+ //at the end we always un-mute the token writer
+ mutableTokenWriter.setMuted(false);
}
}
diff --git a/src/main/java/spoon/reflect/visitor/printer/change/DirectPrinterHelper.java b/src/main/java/spoon/reflect/visitor/printer/change/DirectPrinterHelper.java
index d31089e2dcf..21ecd852fd3 100644
--- a/src/main/java/spoon/reflect/visitor/printer/change/DirectPrinterHelper.java
+++ b/src/main/java/spoon/reflect/visitor/printer/change/DirectPrinterHelper.java
@@ -29,15 +29,20 @@ class DirectPrinterHelper extends PrinterHelper {
}
/**
- * Prints `str` directly into output buffer ignoring any Environment rules.
- * @param str to be printed string
+ * Prints `text` directly into output buffer ignoring any Environment rules.
+ * @param text to be printed string
*/
- void directPrint(String str) {
+ void directPrint(String text) {
autoWriteTabs();
- sbf.append(str);
+ sbf.append(text);
}
- public void setShouldWriteTabs(boolean shouldWriteTabs) {
+ /**
+ * Allows to set the protected field of {@link PrinterHelper}.
+ *
+ * @param shouldWriteTabs true if we just printed EndOfLine and we should pring tabs if next character is not another EndOfLine
+ */
+ void setShouldWriteTabs(boolean shouldWriteTabs) {
this.shouldWriteTabs = shouldWriteTabs;
}
}
diff --git a/src/main/java/spoon/reflect/visitor/printer/change/FragmentType.java b/src/main/java/spoon/reflect/visitor/printer/change/FragmentType.java
index 702a7c1baad..e790913b9c9 100644
--- a/src/main/java/spoon/reflect/visitor/printer/change/FragmentType.java
+++ b/src/main/java/spoon/reflect/visitor/printer/change/FragmentType.java
@@ -27,7 +27,7 @@ public enum FragmentType {
*/
MAIN_FRAGMENT,
/**
- * modifiers of an Type or Variable. {@link DeclarationSourcePosition#getModifierSourceStart()}, {@link DeclarationSourcePosition#getModifierSourceEnd()}
+ * modifiers and annotations of an Type, Executable or Variable. {@link DeclarationSourcePosition#getModifierSourceStart()}, {@link DeclarationSourcePosition#getModifierSourceEnd()}
*/
MODIFIERS,
/**
diff --git a/src/main/java/spoon/reflect/visitor/printer/change/MutableTokenWriter.java b/src/main/java/spoon/reflect/visitor/printer/change/MutableTokenWriter.java
index 9f1d3fc4727..47540825454 100644
--- a/src/main/java/spoon/reflect/visitor/printer/change/MutableTokenWriter.java
+++ b/src/main/java/spoon/reflect/visitor/printer/change/MutableTokenWriter.java
@@ -32,17 +32,24 @@ class MutableTokenWriter implements TokenWriter {
MutableTokenWriter(Environment env) {
super();
- this.delegate = new DefaultTokenWriter(new DirectPrinterHelper(env));;
+ this.delegate = new DefaultTokenWriter(new DirectPrinterHelper(env));
}
+ /**
+ * @return true if tokens are ignored. false if they are forwarded to `delegate`
+ */
boolean isMuted() {
return muted;
}
+ /**
+ * @param muted true if tokens are ignored. false if they are forwarded to `delegate`
+ */
void setMuted(boolean muted) {
this.muted = muted;
}
+ @Override
public TokenWriter writeSeparator(String token) {
if (isMuted()) {
getPrinterHelper().setShouldWriteTabs(false);
@@ -51,6 +58,7 @@ public TokenWriter writeSeparator(String token) {
delegate.writeSeparator(token);
return this;
}
+ @Override
public TokenWriter writeOperator(String token) {
if (isMuted()) {
getPrinterHelper().setShouldWriteTabs(false);
@@ -59,6 +67,7 @@ public TokenWriter writeOperator(String token) {
delegate.writeOperator(token);
return this;
}
+ @Override
public TokenWriter writeLiteral(String token) {
if (isMuted()) {
getPrinterHelper().setShouldWriteTabs(false);
@@ -67,6 +76,7 @@ public TokenWriter writeLiteral(String token) {
delegate.writeLiteral(token);
return this;
}
+ @Override
public TokenWriter writeKeyword(String token) {
if (isMuted()) {
getPrinterHelper().setShouldWriteTabs(false);
@@ -75,6 +85,7 @@ public TokenWriter writeKeyword(String token) {
delegate.writeKeyword(token);
return this;
}
+ @Override
public TokenWriter writeIdentifier(String token) {
if (isMuted()) {
getPrinterHelper().setShouldWriteTabs(false);
@@ -83,6 +94,7 @@ public TokenWriter writeIdentifier(String token) {
delegate.writeIdentifier(token);
return this;
}
+ @Override
public TokenWriter writeCodeSnippet(String token) {
if (isMuted()) {
getPrinterHelper().setShouldWriteTabs(false);
@@ -91,6 +103,7 @@ public TokenWriter writeCodeSnippet(String token) {
delegate.writeCodeSnippet(token);
return this;
}
+ @Override
public TokenWriter writeComment(CtComment comment) {
if (isMuted()) {
getPrinterHelper().setShouldWriteTabs(false);
@@ -99,6 +112,7 @@ public TokenWriter writeComment(CtComment comment) {
delegate.writeComment(comment);
return this;
}
+ @Override
public TokenWriter writeln() {
if (isMuted()) {
getPrinterHelper().setShouldWriteTabs(true);
@@ -107,6 +121,7 @@ public TokenWriter writeln() {
delegate.writeln();
return this;
}
+ @Override
public TokenWriter incTab() {
if (isMuted()) {
return this;
@@ -114,6 +129,7 @@ public TokenWriter incTab() {
delegate.incTab();
return this;
}
+ @Override
public TokenWriter decTab() {
if (isMuted()) {
return this;
@@ -121,15 +137,18 @@ public TokenWriter decTab() {
delegate.decTab();
return this;
}
+ @Override
public DirectPrinterHelper getPrinterHelper() {
return (DirectPrinterHelper) delegate.getPrinterHelper();
}
+ @Override
public void reset() {
if (isMuted()) {
return;
}
delegate.reset();
}
+ @Override
public TokenWriter writeSpace() {
if (isMuted()) {
getPrinterHelper().setShouldWriteTabs(false);
diff --git a/src/main/java/spoon/reflect/visitor/printer/change/PrintAction.java b/src/main/java/spoon/reflect/visitor/printer/change/PrintAction.java
deleted file mode 100644
index 7513854cd20..00000000000
--- a/src/main/java/spoon/reflect/visitor/printer/change/PrintAction.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (C) 2006-2017 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.visitor.printer.change;
-
-/**
- * Represents type of {@link SourceFragment}
- */
-interface PrintAction {
- void run();
-}
diff --git a/src/main/java/spoon/reflect/visitor/printer/change/SourceFragment.java b/src/main/java/spoon/reflect/visitor/printer/change/SourceFragment.java
index 3ee2451244e..e6f0abf126a 100644
--- a/src/main/java/spoon/reflect/visitor/printer/change/SourceFragment.java
+++ b/src/main/java/spoon/reflect/visitor/printer/change/SourceFragment.java
@@ -20,10 +20,12 @@
import java.util.Deque;
import spoon.SpoonException;
+import spoon.reflect.code.CtComment;
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.CtClass;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.visitor.CtScanner;
import spoon.reflect.visitor.printer.change.SourcePositionUtils.FragmentDescriptor;
@@ -31,6 +33,9 @@
/**
* Represents a part of source code of an {@link CtElement}
+ * It is connected into a tree of {@link SourceFragment}s.
+ * That tree can be build by {@link CompilationUnit#getRootSourceFragment()}
+ * And the tree of {@link SourceFragment}s related to one element can be returned by {@link CompilationUnit#getSourceFragment(CtElement)}
*/
public class SourceFragment {
@@ -42,10 +47,28 @@ public class SourceFragment {
private SourceFragment nextSibling;
private SourceFragment firstChild;
- public SourceFragment(CtElement element, SourcePosition sourcePosition) {
- this(element, sourcePosition, FragmentType.MAIN_FRAGMENT);
+ /**
+ * Creates a main fragment of {@link CtElement}
+ * Note: it automatically creates child fragments if `sourcePosition`
+ * is instance of {@link DeclarationSourcePosition} or {@link BodyHolderSourcePosition}
+ *
+ * @param element target {@link CtElement}
+ */
+ public SourceFragment(CtElement element) {
+ this(element, element.getPosition(), FragmentType.MAIN_FRAGMENT);
+ }
+ /**
+ * creates a main fragment for {@link SourcePosition}
+ * Note: it automatically creates child fragments if `sourcePosition`
+ * is instance of {@link DeclarationSourcePosition} or {@link BodyHolderSourcePosition}
+ *
+ * @param sourcePosition target {@link SourcePosition}
+ */
+ public SourceFragment(SourcePosition sourcePosition) {
+ this(null, sourcePosition, FragmentType.MAIN_FRAGMENT);
}
- public SourceFragment(CtElement element, SourcePosition sourcePosition, FragmentType fragmentType) {
+
+ private SourceFragment(CtElement element, SourcePosition sourcePosition, FragmentType fragmentType) {
super();
this.element = element;
this.sourcePosition = sourcePosition;
@@ -55,12 +78,15 @@ public SourceFragment(CtElement element, SourcePosition sourcePosition, Fragment
}
}
+ /**
+ * @return type of fragment
+ */
public FragmentType getFragmentType() {
return fragmentType;
}
/**
- * @return offset of first character which belongs to this fragmen
+ * @return offset of first character which belongs to this fragment
*/
public int getStart() {
switch (fragmentType) {
@@ -81,7 +107,7 @@ public int getStart() {
}
/**
- * @return offset of first next character after this Fragment
+ * @return offset of character after this fragment
*/
public int getEnd() {
switch (fragmentType) {
@@ -104,18 +130,28 @@ public int getEnd() {
throw new SpoonException("Unsupported fragment type: " + fragmentType);
}
+ /**
+ * @return {@link SourcePosition} of this fragment
+ */
public SourcePosition getSourcePosition() {
return sourcePosition;
}
@Override
public String toString() {
+ return "|" + getStart() + ", " + getEnd() + "|" + getSourceCode() + "|";
+ }
+
+ /**
+ * @return origin source code of this fragment
+ */
+ public String getSourceCode() {
return getSourceCode(getStart(), getEnd());
}
/**
- * @param start start offset
- * @param end end offset (after last element)
+ * @param start start offset relative to compilation unit
+ * @param end end offset (after last character) relative to compilation unit
* @return source code of this Fragment between start/end offsets
*/
public String getSourceCode(int start, int end) {
@@ -129,10 +165,16 @@ public String getSourceCode(int start, int end) {
return null;
}
+ /**
+ * @return true if the attribute of {@link CtElement} whose source code is in this fragment is modified
+ */
public boolean isModified() {
return modified;
}
+ /**
+ * @param modified true if the attribute of {@link CtElement} whose source code is in this fragment is modified
+ */
public void setModified(boolean modified) {
this.modified = modified;
}
@@ -145,13 +187,14 @@ private boolean isFromSameSource(SourcePosition position) {
}
/**
- * Builds tree of {@link SourcePosition} elements
+ * Builds tree of {@link SourcePosition}s of `element` and all it's children
* @param element the root element of the tree
*/
- public void addSourceFragments(CtElement element) {
+ public void addTreeOfSourceFragmentsOfElement(CtElement element) {
SourcePosition sp = element.getPosition();
Deque parents = new ArrayDeque<>();
parents.push(this);
+ //scan all children of `element` and build tree of SourceFragments
new CtScanner() {
int noSource = 0;
@Override
@@ -173,20 +216,43 @@ protected void exit(CtElement e) {
}
}.scan(element);
}
- private static SourceFragment addChild(SourceFragment thisFragment, CtElement otherElement) {
+ /**
+ * @param parentFragment the parent {@link SourceFragment}, which will receive {@link SourceFragment} made for `otherElement`
+ * @param otherElement {@link CtElement} whose {@link SourceFragment} has to be added to `parentFragment`
+ * @return
+ */
+ private SourceFragment addChild(SourceFragment parentFragment, CtElement otherElement) {
SourcePosition otherSourcePosition = otherElement.getPosition();
if (otherSourcePosition instanceof SourcePositionImpl && otherSourcePosition.getCompilationUnit() != null) {
SourcePositionImpl childSPI = (SourcePositionImpl) otherSourcePosition;
- if (thisFragment.sourcePosition != childSPI) {
- if (thisFragment.isFromSameSource(otherSourcePosition)) {
- SourceFragment otherFragment = new SourceFragment(otherElement, otherSourcePosition, FragmentType.MAIN_FRAGMENT);
+ if (parentFragment.sourcePosition != childSPI) {
+ if (parentFragment.isFromSameSource(otherSourcePosition)) {
+ SourceFragment otherFragment = new SourceFragment(otherElement);
//parent and child are from the same file. So we can connect their positions into one tree
- CMP cmp = thisFragment.compare(otherFragment);
+ CMP cmp = parentFragment.compare(otherFragment);
if (cmp == CMP.OTHER_IS_CHILD) {
//child belongs under parent - OK
- thisFragment.addChild(otherFragment);
+ parentFragment.addChild(otherFragment);
+ return otherFragment;
} else {
+ if (cmp == CMP.OTHER_IS_AFTER || cmp == CMP.OTHER_IS_BEFORE) {
+ if (otherElement instanceof CtComment) {
+ /*
+ * comments of elements are sometime not included in source position of element.
+ * because comments are ignored tokens for java compiler, which computes start/end of elements
+ * Example:
+ *
+ * //a comment
+ * aStatement();
+ *
+ * No problem. Simply add comment at correct position into SourceFragment tree, starting from root
+ */
+ addChild(otherFragment);
+ return otherFragment;
+ }
+ }
//the source position of child element is not included in source position of parent element
+ //I (Pavel) am not sure how to handle it, so let's wait until it happens...
// if (otherElement instanceof CtAnnotation>) {
// /*
// * it can happen for annotations of type TYPE_USE and FIELD
@@ -196,11 +262,10 @@ private static SourceFragment addChild(SourceFragment thisFragment, CtElement ot
// return null;
// }
//something is wrong ...
- SourcePosition.class.getClass();
- throw new SpoonException("TODO");
+ throw new SpoonException("The SourcePosition of elements are not consistent\nparentFragment: " + parentFragment + "\notherFragment: " + otherFragment);
}
} else {
- throw new SpoonException("SourcePosition from unexpected compilation unit: " + otherSourcePosition + " expected is: " + thisFragment.sourcePosition);
+ throw new SpoonException("SourcePosition from unexpected compilation unit: " + otherSourcePosition + " expected is: " + parentFragment.sourcePosition);
}
}
//else these two elements has same instance of SourcePosition.
@@ -213,6 +278,7 @@ private static SourceFragment addChild(SourceFragment thisFragment, CtElement ot
/**
* adds `other` {@link SourceFragment} into tree of {@link SourceFragment}s represented by this root element
+ *
* @param other to be added {@link SourceFragment}
* @return new root of the tree of the {@link SourceFragment}s. It can be be this or `other`
*/
@@ -319,6 +385,9 @@ private CMP compare(SourceFragment other) {
throw new SpoonException("Cannot compare this: [" + getStart() + ", " + getEnd() + "] with other: [\"" + other.getStart() + "\", \"" + other.getEnd() + "\"]");
}
+ /**
+ * creates child fragments for {@link DeclarationSourcePosition} and {@link BodyHolderSourcePosition}
+ */
private void createChildFragments() {
if (sourcePosition instanceof DeclarationSourcePosition) {
DeclarationSourcePosition dsp = (DeclarationSourcePosition) sourcePosition;
@@ -348,9 +417,17 @@ private void createChildFragments() {
}
}
}
+
+ /**
+ * @return {@link SourceFragment} which belongs to the same parent and is next in the sources
+ */
public SourceFragment getNextSibling() {
return nextSibling;
}
+
+ /**
+ * @return {@link SourceFragment}, which is first child of this fragment
+ */
public SourceFragment getFirstChild() {
return firstChild;
}
@@ -361,7 +438,7 @@ public SourceFragment getFirstChild() {
*
* @param start the start offset of this fragment
* @param end the offset of next character after the end of this fragment
- * @return SourceFragment which represents the root of the CtElement whose sources are in interval [start, end]
+ * @return {@link SourceFragment} which represents the root of the CtElement whose sources are in interval [start, end]
*/
public SourceFragment getSourceFragmentOf(int start, int end) {
int myEnd = getEnd();
@@ -400,7 +477,8 @@ private SourceFragment getRootFragmentOfElement(SourceFragment childFragment) {
return childFragment;
}
/**
- * @return {@link CtElement} whose source code is contained in this fragment
+ * @return {@link CtElement} whose source code is contained in this fragment.
+ * May be null
*/
public CtElement getElement() {
return element;
@@ -408,7 +486,9 @@ public CtElement getElement() {
/**
* @return direct child {@link SourceFragment} if it has same element like this.
- * It means that this {@link SourceFragment} knows the source parts of this element
+ * It means that this {@link SourceFragment} knows the source parts of this element.
+ * E.g. {@link SourceFragment} of {@link CtClass} has fragments for modifiers, name, body, etc.
+ * So in this case it returns first child fragment, which are modifiers fragment of {@link CtClass}.
* Else it returns null.
*/
public SourceFragment getChildFragmentOfSameElement() {
diff --git a/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContext.java b/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContext.java
index d2a110e43c0..c375a5c87f8 100644
--- a/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContext.java
+++ b/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContext.java
@@ -4,6 +4,9 @@
import spoon.reflect.path.CtRole;
import spoon.reflect.visitor.DefaultJavaPrettyPrinter;
+/**
+ * Knows how to handle actually printed {@link CtElement} or it's part
+ */
abstract class SourceFragmentContext {
/**
* Called when TokenWriter token is sent by {@link DefaultJavaPrettyPrinter}
diff --git a/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContextList.java b/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContextList.java
index aae7f1a88f5..10442f4d886 100644
--- a/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContextList.java
+++ b/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContextList.java
@@ -4,67 +4,57 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.function.Predicate;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.meta.impl.RoleHandlerHelper;
import spoon.reflect.path.CtRole;
-import spoon.reflect.visitor.printer.change.SourcePositionUtils.FragmentDescriptor;
/**
- * Handles printing of changes of the list of elements
+ * Handles printing of changes of the list of elements.
+ * E.g. list of type members of type
*/
class SourceFragmentContextList extends SourceFragmentContext {
- private final ChangesAwareDefaultJavaPrettyPrinter printer;
+ private final MutableTokenWriter mutableTokenWriter;
private final SourceFragment rootFragment;
- private final Map listElementToFragmentPair = new IdentityHashMap<>();
+ private final Map listElementToPrefixSpace = new IdentityHashMap<>();
private final List separatorActions = new ArrayList<>();
//-1 means print start list separator
//0..n print elements of list
private int elementIndex = -1;
- private static class FragmentPair {
- /**
- * optional spaces before the element (e.g. spaces before the method or field)
- */
- String prefixSpaces;
- /**
- * the fragment of element
- */
- SourceFragment elementFragment;
-
- FragmentPair(String prefixSpaces, SourceFragment elementFragment) {
- super();
- this.prefixSpaces = prefixSpaces;
- this.elementFragment = elementFragment;
- }
- }
-
- SourceFragmentContextList(ChangesAwareDefaultJavaPrettyPrinter changesAwareDefaultJavaPrettyPrinter, CtElement element, SourceFragment rootFragment) {
+ /**
+ * @param mutableTokenWriter {@link MutableTokenWriter}, which is used for printing
+ * @param element the {@link CtElement} whose list attribute is handled
+ * @param rootFragment the {@link SourceFragment}, which represents whole list of elements. E.g. body of method or all type members of type
+ */
+ SourceFragmentContextList(MutableTokenWriter mutableTokenWriter, CtElement element, SourceFragment rootFragment) {
super();
- printer = changesAwareDefaultJavaPrettyPrinter;
+ this.mutableTokenWriter = mutableTokenWriter;
this.rootFragment = rootFragment;
CtRole listRole = rootFragment.fragmentDescriptor.getListRole();
List listElements = RoleHandlerHelper.getRoleHandler(element.getClass(), listRole).asList(element);
for (CtElement ctElement : listElements) {
SourceFragment elementFragment = SourcePositionUtils.getSourceFragmentOfElement(ctElement);
if (elementFragment != null) {
- FragmentPair fragmentPair = createFragmentPair(rootFragment, elementFragment);
- if (fragmentPair != null) {
- listElementToFragmentPair.put(ctElement, fragmentPair);
+ String prefixSpace = getPrefixSpace(rootFragment, elementFragment);
+ if (prefixSpace != null) {
+ listElementToPrefixSpace.put(ctElement, prefixSpace);
}
}
}
}
- private FragmentPair createFragmentPair(SourceFragment rootFragment, SourceFragment elementFragment) {
+ /**
+ * @param rootFragment
+ * @param elementFragment
+ * @return the spaces, new lines and separators located in the list of elements before the `elementFragment`
+ */
+ private String getPrefixSpace(SourceFragment rootFragment, SourceFragment elementFragment) {
SourceFragment child = rootFragment.getFirstChild();
SourceFragment lastChild = null;
while (child != null) {
if (child == elementFragment) {
- return new FragmentPair(
- lastChild == null ? null : rootFragment.getSourceCode(lastChild.getEnd(), elementFragment.getStart()),
- elementFragment);
+ return lastChild == null ? null : rootFragment.getSourceCode(lastChild.getEnd(), elementFragment.getStart());
}
lastChild = child;
child = child.getNextSibling();
@@ -72,30 +62,21 @@ private FragmentPair createFragmentPair(SourceFragment rootFragment, SourceFragm
return null;
}
- boolean testFagmentDescriptor(SourceFragment sourceFragment, Predicate predicate) {
- if (sourceFragment != null) {
- if (sourceFragment.fragmentDescriptor != null) {
- return predicate.test(sourceFragment.fragmentDescriptor);
- }
- }
- return false;
- }
-
@Override
void onTokenWriterToken(String tokenWriterMethodName, String token, Runnable printAction) {
if (elementIndex == -1) {
- if (printer.mutableTokenWriter.isMuted() == false) {
+ if (mutableTokenWriter.isMuted() == false) {
//print list prefix
String prefix = rootFragment.getTextBeforeFirstChild();
if (prefix != null) {
//we have origin source code for that
- printer.mutableTokenWriter.getPrinterHelper().directPrint(prefix);
+ mutableTokenWriter.getPrinterHelper().directPrint(prefix);
//ignore all list prefix tokens
- printer.mutableTokenWriter.setMuted(true);
+ mutableTokenWriter.setMuted(true);
}
}
//print the list prefix actions. It can be more token writer events ...
- //so enter it several times. Note: it may be muted, then these tokens are ignored
+ //so enter it several times, by several calls of onTokenWriterToken by caller. Note: it may be muted, then these tokens are ignored
printAction.run();
} else {
//print list separator
@@ -109,17 +90,17 @@ void onScanElementOnRole(CtElement element, CtRole role, Runnable printAction) {
//the printing of child element must not be muted here
//it can be muted later internally, if element is not modified
//but something in list is modified and we do not know what
- printer.mutableTokenWriter.setMuted(false);
+ mutableTokenWriter.setMuted(false);
elementIndex++;
if (elementIndex > 0) {
// print spaces between elements
- FragmentPair fragmentPair = listElementToFragmentPair.get(element);
- if (fragmentPair != null) {
+ String prefixSpaces = listElementToPrefixSpace.get(element);
+ if (prefixSpaces != null) {
//there is origin fragment for `element`
- if (fragmentPair.prefixSpaces != null) {
+ if (prefixSpaces != null) {
//there are origin spaces before this `element`
//use them
- printer.mutableTokenWriter.getPrinterHelper().directPrint(fragmentPair.prefixSpaces);
+ mutableTokenWriter.getPrinterHelper().directPrint(prefixSpaces);
//forget DJPP spaces
separatorActions.clear();
}
@@ -133,18 +114,18 @@ void onScanElementOnRole(CtElement element, CtRole role, Runnable printAction) {
//run the DJPP scanning action, which we are listening for
printAction.run();
//the child element is printed, now it will print separators or list end
- printer.mutableTokenWriter.setMuted(true);
+ mutableTokenWriter.setMuted(true);
}
@Override
void onParentFinished() {
- //we are the end of the list of elements. Printer just tries to print list suffix and parent fragment detected that
- printer.mutableTokenWriter.setMuted(false);
+ //we are at the end of the list of elements. Printer just tries to print list suffix and parent fragment detected that
+ mutableTokenWriter.setMuted(false);
//print list suffix
String suffix = rootFragment.getTextAfterLastChild();
if (suffix != null) {
//we have origin source code for that list suffix
- printer.mutableTokenWriter.getPrinterHelper().directPrint(suffix);
+ mutableTokenWriter.getPrinterHelper().directPrint(suffix);
separatorActions.clear();
} else {
//printer must print the spaces and suffix. Send collected events
@@ -152,6 +133,10 @@ void onParentFinished() {
}
}
+ /**
+ * print all tokens, which represents separator of items or suffix of last item
+ * and then forget them, so we can collect next tokens.
+ */
private void printStandardSpaces() {
for (Runnable runnable : separatorActions) {
runnable.run();
diff --git a/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContextNormal.java b/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContextNormal.java
index 84c04f019a9..9588bfe4f47 100644
--- a/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContextNormal.java
+++ b/src/main/java/spoon/reflect/visitor/printer/change/SourceFragmentContextNormal.java
@@ -11,7 +11,7 @@ class SourceFragmentContextNormal extends SourceFragmentContext {
/**
*
*/
- private final ChangesAwareDefaultJavaPrettyPrinter printer;
+ private final MutableTokenWriter mutableTokenWriter;
private SourceFragment currentFragment;
private CtElement element;
/**
@@ -19,16 +19,16 @@ class SourceFragmentContextNormal extends SourceFragmentContext {
*/
private SourceFragmentContext childContext;
- SourceFragmentContextNormal(ChangesAwareDefaultJavaPrettyPrinter changesAwareDefaultJavaPrettyPrinter, CtElement element, SourceFragment rootFragment) {
+ SourceFragmentContextNormal(MutableTokenWriter mutableTokenWriter, CtElement element, SourceFragment rootFragment) {
super();
- printer = changesAwareDefaultJavaPrettyPrinter;
+ this.mutableTokenWriter = mutableTokenWriter;
this.element = element;
this.currentFragment = rootFragment;
handlePrinting();
}
- SourceFragmentContextNormal(ChangesAwareDefaultJavaPrettyPrinter changesAwareDefaultJavaPrettyPrinter) {
- printer = changesAwareDefaultJavaPrettyPrinter;
+ SourceFragmentContextNormal() {
+ mutableTokenWriter = null;
currentFragment = null;
}
@@ -52,18 +52,18 @@ void handlePrinting() {
if (currentFragment.isModified() == false) {
//we are going to print not modified fragment
//print origin sources of this fragment directly
- printer.mutableTokenWriter.getPrinterHelper().directPrint(currentFragment.toString());
- printer.mutableTokenWriter.setMuted(true);
+ mutableTokenWriter.getPrinterHelper().directPrint(currentFragment.getSourceCode());
+ mutableTokenWriter.setMuted(true);
} else {
//we are printing modified fragment.
- printer.mutableTokenWriter.setMuted(false);
+ mutableTokenWriter.setMuted(false);
switch (currentFragment.fragmentDescriptor.kind) {
case NORMAL:
//Let it print normally
break;
case LIST:
//we are printing list, create a child context for the list
- childContext = new SourceFragmentContextList(printer, element, currentFragment);
+ childContext = new SourceFragmentContextList(mutableTokenWriter, element, currentFragment);
break;
default:
throw new SpoonException("Unexpected fragment kind " + currentFragment.fragmentDescriptor.kind);
diff --git a/src/main/java/spoon/support/reflect/cu/CompilationUnitImpl.java b/src/main/java/spoon/support/reflect/cu/CompilationUnitImpl.java
index e0b44d627f8..deec05cc606 100644
--- a/src/main/java/spoon/support/reflect/cu/CompilationUnitImpl.java
+++ b/src/main/java/spoon/support/reflect/cu/CompilationUnitImpl.java
@@ -281,9 +281,9 @@ public void setAutoImport(boolean autoImport) {
public SourceFragment getRootSourceFragment() {
if (rootFragment == null) {
String originSourceCode = getOriginalSourceCode();
- rootFragment = new SourceFragment(null, new SourcePositionImpl(this, 0, originSourceCode.length() - 1, getLineSeparatorPositions()));
+ rootFragment = new SourceFragment(new SourcePositionImpl(this, 0, originSourceCode.length() - 1, getLineSeparatorPositions()));
for (CtType> ctType : declaredTypes) {
- rootFragment.addSourceFragments(ctType);
+ rootFragment.addTreeOfSourceFragmentsOfElement(ctType);
}
}
return rootFragment;
diff --git a/src/test/java/spoon/test/position/SourceFragmentTest.java b/src/test/java/spoon/test/position/SourceFragmentTest.java
index 057c30e477a..b32f6652e2d 100644
--- a/src/test/java/spoon/test/position/SourceFragmentTest.java
+++ b/src/test/java/spoon/test/position/SourceFragmentTest.java
@@ -19,7 +19,7 @@ public class SourceFragmentTest {
@Test
public void testSourcePositionFragment() throws Exception {
SourcePosition sp = new SourcePositionImpl(null, 10, 20, null);
- SourceFragment sf = new SourceFragment(null, sp);
+ SourceFragment sf = new SourceFragment(sp);
assertEquals(10, sf.getStart());
assertEquals(21, sf.getEnd());
assertSame(sp, sf.getSourcePosition());
@@ -30,7 +30,7 @@ public void testSourcePositionFragment() throws Exception {
@Test
public void testDeclarationSourcePositionFragment() throws Exception {
SourcePosition sp = new DeclarationSourcePositionImpl(null, 100, 110, 90, 95, 90, 130, null);
- SourceFragment sf = new SourceFragment(null, sp);
+ SourceFragment sf = new SourceFragment(sp);
assertEquals(90, sf.getStart());
assertEquals(131, sf.getEnd());
assertSame(sp, sf.getSourcePosition());
@@ -63,7 +63,7 @@ public void testDeclarationSourcePositionFragment() throws Exception {
@Test
public void testBodyHolderSourcePositionFragment() throws Exception {
SourcePosition sp = new BodyHolderSourcePositionImpl(null, 100, 110, 90, 95, 90, 130, 120, 130, null);
- SourceFragment sf = new SourceFragment(null, sp);
+ SourceFragment sf = new SourceFragment(sp);
assertEquals(90, sf.getStart());
assertEquals(131, sf.getEnd());
assertSame(sp, sf.getSourcePosition());
@@ -158,6 +158,6 @@ public void testLocalizationOfSourceFragment() throws Exception {
private SourceFragment createFragment(int start, int end) {
- return new SourceFragment(null, new SourcePositionImpl(null, start, end - 1, null));
+ return new SourceFragment(new SourcePositionImpl(null, start, end - 1, null));
}
}