Skip to content

Commit

Permalink
introducing QualifiedProperty
Browse files Browse the repository at this point in the history
  • Loading branch information
ftomassetti committed Feb 23, 2017
1 parent dec68cf commit 40d239e
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 50 deletions.
Expand Up @@ -43,6 +43,15 @@ Node getChild() {
return child;
}

@Override
boolean isToken(int tokenKind) {
return false;
}

NodeText getNodeTextForWrappedNode() {
return lexicalPreservingPrinter.getOrCreateNodeText(child);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand All @@ -67,12 +76,4 @@ public String toString() {
return "ChildTextElement{" + child + '}';
}

@Override
boolean isToken(int tokenKind) {
return false;
}

NodeText getNodeTextForWrappedNode() {
return lexicalPreservingPrinter.getOrCreateNodeText(child);
}
}
Expand Up @@ -25,7 +25,9 @@
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.observer.AstObserver;
import com.github.javaparser.ast.observer.ObservableProperty;
import com.github.javaparser.ast.observer.PropagatingAstObserver;
Expand All @@ -43,6 +45,8 @@
import java.util.*;
import java.util.stream.Collectors;

import static com.github.javaparser.utils.Utils.uncapitalize;

/**
* A Lexical Preserving Printer is used to capture all the lexical information while parsing, update them when
* operating on the AST and then used them to produce code.
Expand All @@ -60,6 +64,9 @@ private interface Inserter {
void insert(Node parent, Node child);
}

/**
* How should I adapt the whitespace around the element when inserting it?
*/
private enum InsertionMode {
PLAIN,
ON_ITS_OWN_LINE
Expand All @@ -72,8 +79,13 @@ private enum InsertionMode {
/**
* Parse the code and setup the LexicalPreservingPrinter.
*/
public static <N extends Node> Pair<ParseResult<N>, LexicalPreservingPrinter> setup(ParseStart<N> parseStart, Provider provider) {
public static <N extends Node> Pair<ParseResult<N>, LexicalPreservingPrinter> setup(ParseStart<N> parseStart,
Provider provider) {
ParseResult<N> parseResult = new JavaParser().parse(parseStart, provider);
if (!parseResult.isSuccessful()) {
throw new RuntimeException("Parsing failed, unable to setup the lexical preservation printer: "
+ parseResult.getProblems());
}
LexicalPreservingPrinter lexicalPreservingPrinter = new LexicalPreservingPrinter(parseResult);
return new Pair<>(parseResult, lexicalPreservingPrinter);
}
Expand Down Expand Up @@ -105,13 +117,16 @@ private static AstObserver createObserver(LexicalPreservingPrinter lpp) {
return new PropagatingAstObserver() {
@Override
public void concretePropertyChange(Node observedNode, ObservableProperty property, Object oldValue, Object newValue) {
if (oldValue != null && oldValue.equals(newValue)) {
// Not really a change, ignoring
if ((oldValue != null && oldValue.equals(newValue)) || (oldValue == null && newValue == null)) {
return;
}
if (property == ObservableProperty.RANGE) {
return;
}
NodeText nodeText = lpp.getTextForNode(observedNode);
// Type requires to be handled in a special way because it is basically a fake node, not part of the
// AST
if (property == ObservableProperty.TYPE && observedNode.getParentNode().get() instanceof FieldDeclaration) {
// Here we have the infamous phantom nodes so we need to handle this specially
// We first of all remove all tokens before the variables. We then print
Expand Down Expand Up @@ -283,33 +298,32 @@ private void updateTextBecauseOfRemovedChild(NodeList nodeList, int index, Optio
return;
}
Node parent = parentNode.get();
String key = parent.getClass().getSimpleName() + ":" + findNodeListName(nodeList);
QualifiedProperty property = new QualifiedProperty(parent.getClass(), findNodeListName(nodeList));

switch (key) {
case "MethodDeclaration:Parameters":
if (index == 0 && nodeList.size() > 1) {
// we should remove all the text between the child and the comma
textForNodes.get(parent).removeTextBetween(child, ASTParserConstants.COMMA, true);
}
if (index != 0) {
// we should remove all the text between the child and the comma
textForNodes.get(parent).removeTextBetween(ASTParserConstants.COMMA, child);
}
default:
textForNodes.get(parent).removeElementForChild(child);
if (property.equals(new QualifiedProperty(MethodDeclaration.class, ObservableProperty.PARAMETERS))) {
if (index == 0 && nodeList.size() > 1) {
// we should remove all the text between the child and the comma
textForNodes.get(parent).removeTextBetween(child, ASTParserConstants.COMMA, true);
}
if (index != 0) {
// we should remove all the text between the child and the comma
textForNodes.get(parent).removeTextBetween(ASTParserConstants.COMMA, child);
}
}

textForNodes.get(parent).removeElementForChild(child);
}

private void updateTextBecauseOfAddedChild(NodeList nodeList, int index, Optional<Node> parentNode, Node child) {
if (!parentNode.isPresent()) {
return;
}
Node parent = parentNode.get();
String nodeListName = findNodeListName(nodeList);
QualifiedProperty property = new QualifiedProperty(parent.getClass(), findNodeListName(nodeList));

if (index == 0) {
// First element of the list, special treatment
Inserter inserter = getPositionFinder(parent.getClass(), nodeListName, parent, nodeList);
Inserter inserter = getPositionFinder(property, parent, nodeList);
inserter.insert(parent, child);
} else {
// Element inside the list
Expand Down Expand Up @@ -441,7 +455,7 @@ private List<TokenTextElement> findIndentation(Node node) {
// Helper methods
//

private String findNodeListName(NodeList nodeList) {
private ObservableProperty findNodeListName(NodeList nodeList) {
Node parent = nodeList.getParentNodeForChildren();
for (Method m : parent.getClass().getMethods()) {
if (m.getParameterCount() == 0 && m.getReturnType().getCanonicalName().equals(NodeList.class.getCanonicalName())) {
Expand All @@ -452,7 +466,7 @@ private String findNodeListName(NodeList nodeList) {
if (name.startsWith("get")) {
name = name.substring("get".length());
}
return name;
return ObservableProperty.fromCamelCaseName(uncapitalize(name));
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
Expand All @@ -462,30 +476,28 @@ private String findNodeListName(NodeList nodeList) {
throw new IllegalArgumentException();
}

private Inserter getPositionFinder(Class<?> parentClass, String nodeListName, Node parent, NodeList nodeList) {
String key = String.format("%s:%s", parentClass.getSimpleName(), nodeListName);
switch (key) {
case "ClassOrInterfaceDeclaration:Members":
if (nodeList.isEmpty()) {
getOrCreateNodeText(parent).removeTextBetween(ASTParserConstants.LBRACE, ASTParserConstants.RBRACE);
}
return insertAfter(ASTParserConstants.LBRACE, InsertionMode.ON_ITS_OWN_LINE);
case "FieldDeclaration:Variables":
try {
return insertAfterChild(FieldDeclaration.class.getMethod("getElementType"), Separator.SPACE);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
case "MethodDeclaration:Parameters":
return insertAfter(ASTParserConstants.LPAREN, InsertionMode.PLAIN);
case "BlockStmt:Statements":
if (nodeList.isEmpty()) {
getOrCreateNodeText(parent).removeTextBetween(ASTParserConstants.LBRACE, ASTParserConstants.RBRACE);
}
return insertAfter(ASTParserConstants.LBRACE, InsertionMode.ON_ITS_OWN_LINE);
private Inserter getPositionFinder(QualifiedProperty property, Node parent, NodeList nodeList) {
if (property.equals(new QualifiedProperty(ClassOrInterfaceDeclaration.class, ObservableProperty.MEMBERS))) {
if (nodeList.isEmpty()) {
getOrCreateNodeText(parent).removeTextBetween(ASTParserConstants.LBRACE, ASTParserConstants.RBRACE);
}
return insertAfter(ASTParserConstants.LBRACE, InsertionMode.ON_ITS_OWN_LINE);
} else if (property.equals(new QualifiedProperty(FieldDeclaration.class, ObservableProperty.VARIABLES))) {
try {
return insertAfterChild(FieldDeclaration.class.getMethod("getElementType"), Separator.SPACE);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else if (property.equals(new QualifiedProperty(MethodDeclaration.class, ObservableProperty.PARAMETERS))) {
return insertAfter(ASTParserConstants.LPAREN, InsertionMode.PLAIN);
} else if (property.equals(new QualifiedProperty(BlockStmt.class, ObservableProperty.STATEMENTS))) {
if (nodeList.isEmpty()) {
getOrCreateNodeText(parent).removeTextBetween(ASTParserConstants.LBRACE, ASTParserConstants.RBRACE);
}
return insertAfter(ASTParserConstants.LBRACE, InsertionMode.ON_ITS_OWN_LINE);
} else {
throw new UnsupportedOperationException("I do not know how to find the position of " + property);
}

throw new UnsupportedOperationException(key);
}

// Visible for testing
Expand Down
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2007-2010 Júlio Vilmar Gesser.
* Copyright (C) 2011, 2013-2016 The JavaParser Team.
*
* This file is part of JavaParser.
*
* JavaParser can be used either under the terms of
* a) the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* b) the terms of the Apache License
*
* You should have received a copy of both licenses in LICENCE.LGPL and
* LICENCE.APACHE. Please refer to those files for details.
*
* JavaParser 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
* GNU Lesser General Public License for more details.
*/

package com.github.javaparser.printer.lexicalpreservation;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.observer.ObservableProperty;

class QualifiedProperty {
private Class<? extends Node> containerClass;
private ObservableProperty observableProperty;

QualifiedProperty(Class<? extends Node> containerClass, ObservableProperty observableProperty) {
this.containerClass = containerClass;
this.observableProperty = observableProperty;
}

@Override
public String toString() {
return "QualifiedProperty{" +
"containerClass=" + containerClass +
", observableProperty=" + observableProperty +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

QualifiedProperty that = (QualifiedProperty) o;

if (!containerClass.getCanonicalName().equals(that.containerClass.getCanonicalName())) return false;
return observableProperty == that.observableProperty;

}

@Override
public int hashCode() {
int result = containerClass.getCanonicalName().hashCode();
result = 31 * result + observableProperty.hashCode();
return result;
}
}

0 comments on commit 40d239e

Please sign in to comment.