Skip to content

Commit

Permalink
tree of SourceFragments
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky committed Apr 2, 2018
1 parent 8b58fc7 commit 0ea3ea4
Show file tree
Hide file tree
Showing 19 changed files with 738 additions and 289 deletions.
Expand Up @@ -123,7 +123,12 @@ CtRole getScannedRole() {
}
}

private void onChange(CtElement currentElement, CtRole role) {
/**
* Called whenever anything changes in the spoon model
* @param currentElement the modified element
* @param role the modified attribute of that element
*/
protected void onChange(CtElement currentElement, CtRole role) {
Set<CtRole> roles = elementToChangeRole.get(currentElement);
if (roles == null) {
roles = new HashSet<>();
Expand Down
@@ -0,0 +1,39 @@
/**
* 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.experimental.modelobs;

import spoon.reflect.cu.CompilationUnit;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.path.CtRole;
import spoon.reflect.visitor.printer.change.SourceFragment;

/**
* Listens on changes on the spoon model and remembers them
* It builds a tree of {@link SourceFragment}s of {@link CompilationUnit} of the modified element
* lazily before the element is changed
*/
public class SourceFragmentsTreeCreatingChangeCollector extends ChangeCollector {
@Override
protected void onChange(CtElement currentElement, CtRole role) {
CompilationUnit cu = currentElement.getPosition().getCompilationUnit();
if (cu != null) {
//build a tree of SourceFragments of compilation unit of the modified element before the first change
cu.getRootSourceFragment();
}
super.onChange(currentElement, role);
}
}
26 changes: 26 additions & 0 deletions src/main/java/spoon/reflect/cu/CompilationUnit.java
Expand Up @@ -20,6 +20,8 @@
import spoon.reflect.declaration.CtModule;
import spoon.reflect.declaration.CtPackage;
import spoon.reflect.declaration.CtType;
import spoon.reflect.visitor.printer.change.SourceFragment;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtImport;

import java.io.File;
Expand Down Expand Up @@ -55,6 +57,16 @@ enum UNIT_TYPE {
*/
void setFile(File file);

/**
* @return array of offsets in the origin source file, where occurs line separator
*/
int[] getLineSeparatorPositions();

/**
* @param lineSeparatorPositions array of offsets in the origin source file, where occurs line separator
*/
void setLineSeparatorPositions(int[] lineSeparatorPositions);

/**
* Gets all binary (.class) files that corresponds to this compilation unit
* and have been created by calling
Expand Down Expand Up @@ -149,4 +161,18 @@ enum UNIT_TYPE {
*/
void setImports(Collection<CtImport> imports);

/**
* @return root {@link SourceFragment} of the tree of all the source fragments of origin source code
* Note: this method creates tree of {@link SourceFragment}s ... it can be a lot of instances
* If the tree of {@link SourceFragment}s is needed then it MUST be created
* BEFORE the Spoon model of this {@link CompilationUnit} is changed.
* Otherwise there might be missing some {@link SourceFragment}s when {@link CtElement}s are deleted
*/
SourceFragment getRootSourceFragment();

/**
* @param element the {@link CtElement} whose origin source code is needed
* @return {@link SourceFragment} which mirrors the origin source code of the `element` or null.
*/
SourceFragment getSourceFragment(CtElement element);
}
12 changes: 0 additions & 12 deletions src/main/java/spoon/reflect/cu/SourcePosition.java
Expand Up @@ -83,16 +83,4 @@ public interface SourcePosition extends Serializable {
* Gets the index at which the position starts in the source file.
*/
int getSourceStart();

/**
* @return {@link SourcePosition} of next element in the origin source code.
* If there is no one, then it returns null. It never returns {@link NoSourcePosition}
*/
SourcePosition getNextSibling();

/**
* @return {@link SourcePosition} of first child element in the origin source code.
* If there is no one, then it returns null. It never returns {@link NoSourcePosition}
*/
SourcePosition getFirstChild();
}
10 changes: 0 additions & 10 deletions src/main/java/spoon/reflect/cu/position/NoSourcePosition.java
Expand Up @@ -73,14 +73,4 @@ public int getSourceStart() {
public String toString() {
return "(unknown file)";
}

@Override
public SourcePosition getFirstChild() {
return null;
}

@Override
public SourcePosition getNextSibling() {
return null;
}
}
Expand Up @@ -20,9 +20,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.function.Predicate;

import spoon.SpoonException;
Expand Down Expand Up @@ -103,30 +101,36 @@ protected void onTokenWriterWrite(String tokenWriterMethodName, String token, Pr
}

private class SourceFragmentContext {
final List<SourceFragment> fragments;
int currentFragmentIdx = -1;
private SourceFragment currentFragment;
private CtElement element;

SourceFragmentContext(List<SourceFragment> fragments) {
SourceFragmentContext(CtElement element, SourceFragment rootFragment) {
super();
this.fragments = fragments;
this.element = element;
this.currentFragment = rootFragment;
}

SourceFragmentContext() {
this.fragments = Collections.emptyList();
currentFragmentIdx = 0;
currentFragment = null;
}

SourceFragment getNextFragment() {
if (currentFragment != null) {
return currentFragment.getNextFragmentOfSameElement();
}
return null;
}

/**
* Called when next fragment is going to be printed
*/
void nextFragment() {
currentFragmentIdx++;
if (currentFragmentIdx < fragments.size()) {
SourceFragment fragment = fragments.get(currentFragmentIdx);
if (fragment.isModified() == false) {
currentFragment = getNextFragment();
if (currentFragment != null) {
if (currentFragment.isModified() == false) {
//we are going to print not modified fragment
//print origin sources of this fragment directly
mutableTokenWriter.getPrinterHelper().directPrint(fragment.toString());
mutableTokenWriter.getPrinterHelper().directPrint(currentFragment.toString());
mutableTokenWriter.setMuted(true);
} else {
//we are printing modified fragment. Let it print normally
Expand All @@ -135,9 +139,8 @@ void nextFragment() {
}
}

boolean testFagmentDescriptor(int fragmentIdx, Predicate<FragmentDescriptor> predicate) {
if (fragmentIdx < fragments.size()) {
SourceFragment sourceFragment = fragments.get(fragmentIdx);
boolean testFagmentDescriptor(SourceFragment sourceFragment, Predicate<FragmentDescriptor> predicate) {
if (sourceFragment != null) {
if (sourceFragment.fragmentDescriptor != null) {
return predicate.test(sourceFragment.fragmentDescriptor);
}
Expand All @@ -146,20 +149,20 @@ boolean testFagmentDescriptor(int fragmentIdx, Predicate<FragmentDescriptor> pre
}

void onTokenWriterToken(String tokenWriterMethodName, String token, PrintAction printAction) throws Exception {
if (testFagmentDescriptor(currentFragmentIdx + 1, fd -> fd.isTriggeredByToken(true, tokenWriterMethodName, token))) {
if (testFagmentDescriptor(getNextFragment(), fd -> fd.isTriggeredByToken(true, tokenWriterMethodName, token))) {
//yes, the next fragment should be activated before printAction
nextFragment();
}
//run the print action, which we are listening for
printAction.run();
if (testFagmentDescriptor(currentFragmentIdx, fd -> fd.isTriggeredByToken(false, tokenWriterMethodName, token))) {
if (testFagmentDescriptor(currentFragment, fd -> fd.isTriggeredByToken(false, tokenWriterMethodName, token))) {
//yes, the next fragment should be activated before printAction
nextFragment();
}
}

void onScanRole(CtRole role, PrintAction printAction) {
if (testFagmentDescriptor(currentFragmentIdx + 1, fd -> fd.isStartedByScanRole(role))) {
if (testFagmentDescriptor(getNextFragment(), fd -> fd.isStartedByScanRole(role))) {
//yes, the next fragment should be activated before printAction
nextFragment();
}
Expand All @@ -173,7 +176,6 @@ void onScanRole(CtRole role, PrintAction printAction) {
}
// {
// //check if current fragment has to be finished after this action
// SourceFragment currentFragment = fragments.get(currentFragmentIdx);
// if (currentFragment.fragmentDescriptor.isFinishedByScanRole(role)) {
// nextFragment();
// }
Expand Down Expand Up @@ -208,16 +210,16 @@ private void scanInternal(CtElement element) {
}
//it is not muted yet, so some this element or any sibling was modified
//detect SourceFragments of element and whether they are modified or not
List<SourceFragment> fragments = SourcePositionUtils.getSourceFragmentsOfElement(changeCollector, element);
if (fragments.isEmpty()) {
SourceFragment rootFragmentOfElement = SourcePositionUtils.getSourceFragmentsOfElement(changeCollector, element);
if (rootFragmentOfElement == null) {
//we have no origin sources. Use normal printing
sourceFragmentContextStack.push(EMPTY_FRAGMENT_CONTEXT);
super.scan(element);
sourceFragmentContextStack.pop();
return;
}
try {
SourceFragmentContext sfx = new SourceFragmentContext(fragments);
SourceFragmentContext sfx = new SourceFragmentContext(element, rootFragmentOfElement);
sourceFragmentContextStack.push(sfx);
//move to the first fragment - and handle printing of modified or not modified content
sfx.nextFragment();
Expand Down

0 comments on commit 0ea3ea4

Please sign in to comment.