aboutsummaryrefslogtreecommitdiffstats
path: root/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/LexicalDifferenceCalculator.java
diff options
context:
space:
mode:
Diffstat (limited to 'javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/LexicalDifferenceCalculator.java')
-rw-r--r--javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/LexicalDifferenceCalculator.java332
1 files changed, 332 insertions, 0 deletions
diff --git a/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/LexicalDifferenceCalculator.java b/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/LexicalDifferenceCalculator.java
new file mode 100644
index 000000000..9263dfd8a
--- /dev/null
+++ b/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/LexicalDifferenceCalculator.java
@@ -0,0 +1,332 @@
+package com.github.javaparser.printer.lexicalpreservation;
+
+import com.github.javaparser.GeneratedJavaParserConstants;
+import com.github.javaparser.ast.Modifier;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.NodeList;
+import com.github.javaparser.ast.expr.StringLiteralExpr;
+import com.github.javaparser.ast.observer.ObservableProperty;
+import com.github.javaparser.printer.ConcreteSyntaxModel;
+import com.github.javaparser.printer.Printable;
+import com.github.javaparser.printer.SourcePrinter;
+import com.github.javaparser.printer.concretesyntaxmodel.*;
+import com.github.javaparser.printer.lexicalpreservation.changes.*;
+
+import java.util.*;
+
+class LexicalDifferenceCalculator {
+
+ /**
+ * The ConcreteSyntaxModel represents the general format. This model is a calculated version of the ConcreteSyntaxModel,
+ * with no condition, no lists, just tokens and node children.
+ */
+ static class CalculatedSyntaxModel {
+ final List<CsmElement> elements;
+
+ CalculatedSyntaxModel(List<CsmElement> elements) {
+ this.elements = elements;
+ }
+
+ public CalculatedSyntaxModel from(int index) {
+ List<CsmElement> newList = new LinkedList<>();
+ newList.addAll(elements.subList(index, elements.size()));
+ return new CalculatedSyntaxModel(newList);
+ }
+
+ @Override
+ public String toString() {
+ return "CalculatedSyntaxModel{" +
+ "elements=" + elements +
+ '}';
+ }
+
+ CalculatedSyntaxModel sub(int start, int end) {
+ return new CalculatedSyntaxModel(elements.subList(start, end));
+ }
+
+ void removeIndentationElements() {
+ elements.removeIf(el -> el instanceof CsmIndent || el instanceof CsmUnindent);
+ }
+ }
+
+ static class CsmChild implements CsmElement {
+ private final Node child;
+
+ public Node getChild() {
+ return child;
+ }
+
+ CsmChild(Node child) {
+ this.child = child;
+ }
+
+ @Override
+ public void prettyPrint(Node node, SourcePrinter printer) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ return "child(" + child.getClass().getSimpleName()+")";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CsmChild csmChild = (CsmChild) o;
+
+ return child.equals(csmChild.child);
+ }
+
+ @Override
+ public int hashCode() {
+ return child.hashCode();
+ }
+ }
+
+ Difference calculateListRemovalDifference(ObservableProperty observableProperty, NodeList nodeList, int index) {
+ Node container = nodeList.getParentNodeForChildren();
+ CsmElement element = ConcreteSyntaxModel.forClass(container.getClass());
+ CalculatedSyntaxModel original = calculatedSyntaxModelForNode(element, container);
+ CalculatedSyntaxModel after = calculatedSyntaxModelAfterListRemoval(element, observableProperty, nodeList, index);
+ return Difference.calculate(original, after);
+ }
+
+ Difference calculateListAdditionDifference(ObservableProperty observableProperty, NodeList nodeList, int index, Node nodeAdded) {
+ Node container = nodeList.getParentNodeForChildren();
+ CsmElement element = ConcreteSyntaxModel.forClass(container.getClass());
+ CalculatedSyntaxModel original = calculatedSyntaxModelForNode(element, container);
+ CalculatedSyntaxModel after = calculatedSyntaxModelAfterListAddition(element, observableProperty, nodeList, index, nodeAdded);
+ return Difference.calculate(original, after);
+ }
+
+ Difference calculateListReplacementDifference(ObservableProperty observableProperty, NodeList nodeList, int index, Node newValue) {
+ Node container = nodeList.getParentNodeForChildren();
+ CsmElement element = ConcreteSyntaxModel.forClass(container.getClass());
+ CalculatedSyntaxModel original = calculatedSyntaxModelForNode(element, container);
+ CalculatedSyntaxModel after = calculatedSyntaxModelAfterListReplacement(element, observableProperty, nodeList, index, newValue);
+ return Difference.calculate(original, after);
+ }
+
+ public void calculatePropertyChange(NodeText nodeText, Node observedNode, ObservableProperty property, Object oldValue, Object newValue) {
+ if (nodeText == null) {
+ throw new NullPointerException();
+ }
+ CsmElement element = ConcreteSyntaxModel.forClass(observedNode.getClass());
+ CalculatedSyntaxModel original = calculatedSyntaxModelForNode(element, observedNode);
+ CalculatedSyntaxModel after = calculatedSyntaxModelAfterPropertyChange(element, observedNode, property, oldValue, newValue);
+ Difference difference = Difference.calculate(original, after);
+ difference.apply(nodeText, observedNode);
+ }
+
+ // Visible for testing
+ CalculatedSyntaxModel calculatedSyntaxModelForNode(CsmElement csm, Node node) {
+ List<CsmElement> elements = new LinkedList<>();
+ calculatedSyntaxModelForNode(csm, node, elements, new NoChange());
+ return new CalculatedSyntaxModel(elements);
+ }
+
+ CalculatedSyntaxModel calculatedSyntaxModelForNode(Node node) {
+ return calculatedSyntaxModelForNode(ConcreteSyntaxModel.forClass(node.getClass()), node);
+ }
+
+ private void calculatedSyntaxModelForNode(CsmElement csm, Node node, List<CsmElement> elements, Change change) {
+ if (csm instanceof CsmSequence) {
+ CsmSequence csmSequence = (CsmSequence) csm;
+ csmSequence.getElements().forEach(e -> calculatedSyntaxModelForNode(e, node, elements, change));
+ } else if (csm instanceof CsmComment) {
+ // nothing to do
+ } else if (csm instanceof CsmSingleReference) {
+ CsmSingleReference csmSingleReference = (CsmSingleReference)csm;
+ Node child;
+ if (change instanceof PropertyChange && ((PropertyChange)change).getProperty() == csmSingleReference.getProperty()) {
+ child = (Node)((PropertyChange)change).getNewValue();
+ } else {
+ child = csmSingleReference.getProperty().getValueAsSingleReference(node);
+ }
+ if (child != null) {
+ elements.add(new CsmChild(child));
+ }
+ } else if (csm instanceof CsmNone) {
+ // nothing to do
+ } else if (csm instanceof CsmToken) {
+ elements.add(csm);
+ } else if (csm instanceof CsmOrphanCommentsEnding) {
+ // nothing to do
+ } else if (csm instanceof CsmList) {
+ CsmList csmList = (CsmList) csm;
+ if (csmList.getProperty().isAboutNodes()) {
+ Object rawValue = change.getValue(csmList.getProperty(), node);
+ NodeList nodeList;
+ if (rawValue instanceof Optional) {
+ Optional optional = (Optional)rawValue;
+ if (optional.isPresent()) {
+ if (!(optional.get() instanceof NodeList)) {
+ throw new IllegalStateException("Expected NodeList, found " + optional.get().getClass().getCanonicalName());
+ }
+ nodeList = (NodeList) optional.get();
+ } else {
+ nodeList = new NodeList();
+ }
+ } else {
+ if (!(rawValue instanceof NodeList)) {
+ throw new IllegalStateException("Expected NodeList, found " + rawValue.getClass().getCanonicalName());
+ }
+ nodeList = (NodeList) rawValue;
+ }
+ if (!nodeList.isEmpty()) {
+ calculatedSyntaxModelForNode(csmList.getPreceeding(), node, elements, change);
+ for (int i = 0; i < nodeList.size(); i++) {
+ if (i != 0) {
+ calculatedSyntaxModelForNode(csmList.getSeparatorPre(), node, elements, change);
+ }
+ elements.add(new CsmChild(nodeList.get(i)));
+ if (i != (nodeList.size() - 1)) {
+ calculatedSyntaxModelForNode(csmList.getSeparatorPost(), node, elements, change);
+ }
+
+ }
+ calculatedSyntaxModelForNode(csmList.getFollowing(), node, elements, change);
+ }
+ } else {
+ Collection collection = (Collection) change.getValue(csmList.getProperty(), node);
+ if (!collection.isEmpty()) {
+ calculatedSyntaxModelForNode(csmList.getPreceeding(), node, elements, change);
+
+ boolean first = true;
+ for (Iterator it = collection.iterator(); it.hasNext(); ) {
+ if (!first) {
+ calculatedSyntaxModelForNode(csmList.getSeparatorPre(), node, elements, change);
+ }
+ Object value = it.next();
+ if (value instanceof Modifier) {
+ Modifier modifier = (Modifier)value;
+ elements.add(new CsmToken(toToken(modifier)));
+ } else {
+ throw new UnsupportedOperationException(it.next().getClass().getSimpleName());
+ }
+ if (it.hasNext()) {
+ calculatedSyntaxModelForNode(csmList.getSeparatorPost(), node, elements, change);
+ }
+ first = false;
+ }
+ calculatedSyntaxModelForNode(csmList.getFollowing(), node, elements, change);
+ }
+ }
+ } else if (csm instanceof CsmConditional) {
+ CsmConditional csmConditional = (CsmConditional) csm;
+ boolean satisfied = change.evaluate(csmConditional, node);
+ if (satisfied) {
+ calculatedSyntaxModelForNode(csmConditional.getThenElement(), node, elements, change);
+ } else {
+ calculatedSyntaxModelForNode(csmConditional.getElseElement(), node, elements, change);
+ }
+ } else if (csm instanceof CsmIndent) {
+ elements.add(csm);
+ } else if (csm instanceof CsmUnindent) {
+ elements.add(csm);
+ } else if (csm instanceof CsmAttribute) {
+ CsmAttribute csmAttribute = (CsmAttribute) csm;
+ Object value = change.getValue(csmAttribute.getProperty(), node);
+ String text = value.toString();
+ if (value instanceof Printable) {
+ text = ((Printable) value).asString();
+ }
+ elements.add(new CsmToken(csmAttribute.getTokenType(node, value.toString()), text));
+ } else if ((csm instanceof CsmString) && (node instanceof StringLiteralExpr)) {
+ elements.add(new CsmToken(GeneratedJavaParserConstants.STRING_LITERAL,
+ "\"" + ((StringLiteralExpr) node).getValue() + "\""));
+ } else if (csm instanceof CsmMix) {
+ CsmMix csmMix = (CsmMix)csm;
+ List<CsmElement> mixElements = new LinkedList<>();
+ csmMix.getElements().forEach(e -> calculatedSyntaxModelForNode(e, node, mixElements, change));
+ elements.add(new CsmMix(mixElements));
+ } else {
+ throw new UnsupportedOperationException(csm.getClass().getSimpleName()+ " " + csm);
+ }
+ }
+
+ private int toToken(Modifier modifier) {
+ switch (modifier) {
+ case PUBLIC:
+ return GeneratedJavaParserConstants.PUBLIC;
+ case PRIVATE:
+ return GeneratedJavaParserConstants.PRIVATE;
+ case PROTECTED:
+ return GeneratedJavaParserConstants.PROTECTED;
+ case STATIC:
+ return GeneratedJavaParserConstants.STATIC;
+ case FINAL:
+ return GeneratedJavaParserConstants.FINAL;
+ case ABSTRACT:
+ return GeneratedJavaParserConstants.ABSTRACT;
+ default:
+ throw new UnsupportedOperationException(modifier.name());
+ }
+ }
+
+ ///
+ /// Methods that calculate CalculatedSyntaxModel
+ ///
+
+ // Visible for testing
+ CalculatedSyntaxModel calculatedSyntaxModelAfterPropertyChange(Node node, ObservableProperty property, Object oldValue, Object newValue) {
+ return calculatedSyntaxModelAfterPropertyChange(ConcreteSyntaxModel.forClass(node.getClass()), node, property, oldValue, newValue);
+ }
+
+ // Visible for testing
+ CalculatedSyntaxModel calculatedSyntaxModelAfterPropertyChange(CsmElement csm, Node node, ObservableProperty property, Object oldValue, Object newValue) {
+ List<CsmElement> elements = new LinkedList<>();
+ calculatedSyntaxModelForNode(csm, node, elements, new PropertyChange(property, oldValue, newValue));
+ return new CalculatedSyntaxModel(elements);
+ }
+
+ // Visible for testing
+ CalculatedSyntaxModel calculatedSyntaxModelAfterListRemoval(CsmElement csm, ObservableProperty observableProperty, NodeList nodeList, int index) {
+ List<CsmElement> elements = new LinkedList<>();
+ Node container = nodeList.getParentNodeForChildren();
+ calculatedSyntaxModelForNode(csm, container, elements, new ListRemovalChange(observableProperty, index));
+ return new CalculatedSyntaxModel(elements);
+ }
+
+ // Visible for testing
+ CalculatedSyntaxModel calculatedSyntaxModelAfterListAddition(CsmElement csm, ObservableProperty observableProperty, NodeList nodeList, int index, Node nodeAdded) {
+ List<CsmElement> elements = new LinkedList<>();
+ Node container = nodeList.getParentNodeForChildren();
+ calculatedSyntaxModelForNode(csm, container, elements, new ListAdditionChange(observableProperty, index, nodeAdded));
+ return new CalculatedSyntaxModel(elements);
+ }
+
+ // Visible for testing
+ CalculatedSyntaxModel calculatedSyntaxModelAfterListAddition(Node container, ObservableProperty observableProperty, int index, Node nodeAdded) {
+ CsmElement csm = ConcreteSyntaxModel.forClass(container.getClass());
+ Object rawValue = observableProperty.getRawValue(container);
+ if (!(rawValue instanceof NodeList)) {
+ throw new IllegalStateException("Expected NodeList, found " + rawValue.getClass().getCanonicalName());
+ }
+ NodeList nodeList = (NodeList)rawValue;
+ return calculatedSyntaxModelAfterListAddition(csm, observableProperty, nodeList, index, nodeAdded);
+ }
+
+ // Visible for testing
+ CalculatedSyntaxModel calculatedSyntaxModelAfterListRemoval(Node container, ObservableProperty observableProperty, int index) {
+ CsmElement csm = ConcreteSyntaxModel.forClass(container.getClass());
+ Object rawValue = observableProperty.getRawValue(container);
+ if (!(rawValue instanceof NodeList)) {
+ throw new IllegalStateException("Expected NodeList, found " + rawValue.getClass().getCanonicalName());
+ }
+ NodeList nodeList = (NodeList)rawValue;
+ return calculatedSyntaxModelAfterListRemoval(csm, observableProperty, nodeList, index);
+ }
+
+ // Visible for testing
+ private CalculatedSyntaxModel calculatedSyntaxModelAfterListReplacement(CsmElement csm, ObservableProperty observableProperty, NodeList nodeList, int index, Node newValue) {
+ List<CsmElement> elements = new LinkedList<>();
+ Node container = nodeList.getParentNodeForChildren();
+ calculatedSyntaxModelForNode(csm, container, elements, new ListReplacementChange(observableProperty, index, newValue));
+ return new CalculatedSyntaxModel(elements);
+ }
+
+}