1d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti/* 2d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * Copyright (C) 2007-2010 Júlio Vilmar Gesser. 3d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * Copyright (C) 2011, 2013-2016 The JavaParser Team. 4d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * 5d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * This file is part of JavaParser. 6d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * 7d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * JavaParser can be used either under the terms of 8d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * a) the GNU Lesser General Public License as published by 9d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * the Free Software Foundation, either version 3 of the License, or 10d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * (at your option) any later version. 11d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * b) the terms of the Apache License 12d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * 13d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * You should have received a copy of both licenses in LICENCE.LGPL and 14d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * LICENCE.APACHE. Please refer to those files for details. 15d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * 16d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * JavaParser is distributed in the hope that it will be useful, 17d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * but WITHOUT ANY WARRANTY; without even the implied warranty of 18d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * GNU Lesser General Public License for more details. 20d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti */ 21d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 22d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettipackage com.github.javaparser.printer.lexicalpreservation; 23d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 24d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport com.github.javaparser.*; 25a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggenimport com.github.javaparser.ast.DataKey; 26d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport com.github.javaparser.ast.Node; 27d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport com.github.javaparser.ast.NodeList; 287e85803c3a66d340e1cbdbc6c98081104ad412c3Federico Tomassettiimport com.github.javaparser.ast.body.VariableDeclarator; 290c57e5931daabf22632ad49f2ad0657df59b858bFederico Tomassettiimport com.github.javaparser.ast.comments.Comment; 30f0f313058c4389cd48052112e3fb906c060be30fFederico Tomassettiimport com.github.javaparser.ast.comments.JavadocComment; 31f8f8a6fc81e2d717568c0ab9895605c1f4b69bbcFederico Tomassettiimport com.github.javaparser.ast.nodeTypes.NodeWithVariables; 32d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport com.github.javaparser.ast.observer.AstObserver; 33d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport com.github.javaparser.ast.observer.ObservableProperty; 34d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport com.github.javaparser.ast.observer.PropagatingAstObserver; 35de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassettiimport com.github.javaparser.ast.type.PrimitiveType; 36d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport com.github.javaparser.ast.visitor.TreeVisitor; 3703df427399267338f87748888d1f921bb9a1c0daFederico Tomassettiimport com.github.javaparser.printer.ConcreteSyntaxModel; 3803df427399267338f87748888d1f921bb9a1c0daFederico Tomassettiimport com.github.javaparser.printer.concretesyntaxmodel.CsmElement; 399e91a1d76dbfb28f2bc7c32028fe459b073ee861Federico Tomassettiimport com.github.javaparser.printer.concretesyntaxmodel.CsmMix; 4003df427399267338f87748888d1f921bb9a1c0daFederico Tomassettiimport com.github.javaparser.printer.concretesyntaxmodel.CsmToken; 41d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport com.github.javaparser.utils.Pair; 4215cb49e74025eb4011187f31735aa4772993fb5dFederico Tomassettiimport com.github.javaparser.utils.Utils; 43d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 44d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport java.io.IOException; 45d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport java.io.StringWriter; 46d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport java.io.Writer; 47d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport java.lang.reflect.InvocationTargetException; 48d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport java.lang.reflect.Method; 49d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassettiimport java.lang.reflect.ParameterizedType; 50d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport java.util.*; 51d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettiimport java.util.stream.Collectors; 52d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 5383d60da8afce695e7356af94c549e30ab585448cDanny van Bruggenimport static com.github.javaparser.GeneratedJavaParserConstants.*; 546a1ee724c337694ee28b2a09af41c236b880ba66Danny van Bruggenimport static com.github.javaparser.TokenTypes.eolTokenKind; 558324ea0bfcac6db2f2ef2c3d12a84744842aa8adDanny van Bruggenimport static com.github.javaparser.utils.Utils.assertNotNull; 56d4db09d04aca5a9df4278652d55d680c4a826efcFederico Tomassettiimport static com.github.javaparser.utils.Utils.decapitalize; 5748789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggenimport static java.util.Comparator.*; 5840d239e3cb84cc77fcd16c3f9fde3f4f8b635419Federico Tomassetti 59d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti/** 60d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * A Lexical Preserving Printer is used to capture all the lexical information while parsing, update them when 619a228915f61892f6ba99fcf1e42e42e1e2622a00Danny van Bruggen * operating on the AST and then used them to reproduce the source code 628324ea0bfcac6db2f2ef2c3d12a84744842aa8adDanny van Bruggen * in its original formatting including the AST changes. 63d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti */ 64d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassettipublic class LexicalPreservingPrinter { 65d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 66a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggen /** 67a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggen * The nodetext for a node is stored in the node's data field. This is the key to set and retrieve it. 68a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggen */ 6948789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen public static final DataKey<NodeText> NODE_TEXT_DATA = new DataKey<NodeText>() { 7048789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen }; 71a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggen 72d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // 73d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // Factory methods 74d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // 75d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 76d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti /** 77d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * Parse the code and setup the LexicalPreservingPrinter. 7848789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen * 796149218487aac50fad2256c8723639671747d7bbDanny van Bruggen * @deprecated use setup(Node) and the static methods on this class. 80d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti */ 8140d239e3cb84cc77fcd16c3f9fde3f4f8b635419Federico Tomassetti public static <N extends Node> Pair<ParseResult<N>, LexicalPreservingPrinter> setup(ParseStart<N> parseStart, 8240d239e3cb84cc77fcd16c3f9fde3f4f8b635419Federico Tomassetti Provider provider) { 83d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti ParseResult<N> parseResult = new JavaParser().parse(parseStart, provider); 8440d239e3cb84cc77fcd16c3f9fde3f4f8b635419Federico Tomassetti if (!parseResult.isSuccessful()) { 8540d239e3cb84cc77fcd16c3f9fde3f4f8b635419Federico Tomassetti throw new RuntimeException("Parsing failed, unable to setup the lexical preservation printer: " 8640d239e3cb84cc77fcd16c3f9fde3f4f8b635419Federico Tomassetti + parseResult.getProblems()); 8740d239e3cb84cc77fcd16c3f9fde3f4f8b635419Federico Tomassetti } 888324ea0bfcac6db2f2ef2c3d12a84744842aa8adDanny van Bruggen LexicalPreservingPrinter lexicalPreservingPrinter = new LexicalPreservingPrinter(parseResult.getResult().get()); 89d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti return new Pair<>(parseResult, lexicalPreservingPrinter); 90d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 91d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 92dd0f96d32e589249fdd05ab40f9d087c79415730Danny van Bruggen /** 93dd0f96d32e589249fdd05ab40f9d087c79415730Danny van Bruggen * Prepares the node so it can be used in the print methods. 94dd0f96d32e589249fdd05ab40f9d087c79415730Danny van Bruggen * The correct order is: 95dd0f96d32e589249fdd05ab40f9d087c79415730Danny van Bruggen * <ol> 9648789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen * <li>Parse some code</li> 9748789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen * <li>Call this setup method on the result</li> 9848789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen * <li>Make changes to the AST as desired</li> 9948789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen * <li>Use one of the print methods on this class to print out the original source code with your changes added</li> 100dd0f96d32e589249fdd05ab40f9d087c79415730Danny van Bruggen * </ol> 10148789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen * 102dd0f96d32e589249fdd05ab40f9d087c79415730Danny van Bruggen * @return the node passed as a parameter for your convenience. 103dd0f96d32e589249fdd05ab40f9d087c79415730Danny van Bruggen */ 104dd0f96d32e589249fdd05ab40f9d087c79415730Danny van Bruggen public static <N extends Node> N setup(N node) { 1058324ea0bfcac6db2f2ef2c3d12a84744842aa8adDanny van Bruggen assertNotNull(node); 1066149218487aac50fad2256c8723639671747d7bbDanny van Bruggen 1078324ea0bfcac6db2f2ef2c3d12a84744842aa8adDanny van Bruggen node.getTokenRange().ifPresent(r -> { 1088324ea0bfcac6db2f2ef2c3d12a84744842aa8adDanny van Bruggen storeInitialText(node); 109d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 1107a48b21bb866b8d4baf2ce50992c0dfd93f4bc3bFederico Tomassetti // Setup observer 1116149218487aac50fad2256c8723639671747d7bbDanny van Bruggen AstObserver observer = createObserver(); 1127a48b21bb866b8d4baf2ce50992c0dfd93f4bc3bFederico Tomassetti 1138324ea0bfcac6db2f2ef2c3d12a84744842aa8adDanny van Bruggen node.registerForSubtree(observer); 1148324ea0bfcac6db2f2ef2c3d12a84744842aa8adDanny van Bruggen }); 115dd0f96d32e589249fdd05ab40f9d087c79415730Danny van Bruggen return node; 116d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 1179a228915f61892f6ba99fcf1e42e42e1e2622a00Danny van Bruggen 1186149218487aac50fad2256c8723639671747d7bbDanny van Bruggen // 1196149218487aac50fad2256c8723639671747d7bbDanny van Bruggen // Constructor and setup 1206149218487aac50fad2256c8723639671747d7bbDanny van Bruggen // 121d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 1226149218487aac50fad2256c8723639671747d7bbDanny van Bruggen /** 1236149218487aac50fad2256c8723639671747d7bbDanny van Bruggen * @deprecated use setup(Node) to prepare a node for lexical preservation, 1246149218487aac50fad2256c8723639671747d7bbDanny van Bruggen * then use the static methods on this class to print it. 1256149218487aac50fad2256c8723639671747d7bbDanny van Bruggen */ 1266149218487aac50fad2256c8723639671747d7bbDanny van Bruggen @Deprecated 1276149218487aac50fad2256c8723639671747d7bbDanny van Bruggen public LexicalPreservingPrinter(Node node) { 1286149218487aac50fad2256c8723639671747d7bbDanny van Bruggen setup(node); 1296149218487aac50fad2256c8723639671747d7bbDanny van Bruggen } 1306149218487aac50fad2256c8723639671747d7bbDanny van Bruggen 1316149218487aac50fad2256c8723639671747d7bbDanny van Bruggen private static AstObserver createObserver() { 132d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti return new PropagatingAstObserver() { 133d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti @Override 134d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti public void concretePropertyChange(Node observedNode, ObservableProperty property, Object oldValue, Object newValue) { 13540d239e3cb84cc77fcd16c3f9fde3f4f8b635419Federico Tomassetti // Not really a change, ignoring 13640d239e3cb84cc77fcd16c3f9fde3f4f8b635419Federico Tomassetti if ((oldValue != null && oldValue.equals(newValue)) || (oldValue == null && newValue == null)) { 137d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti return; 138d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 1395e40a8838a5118df43a847ce8c52dddac0843b22Federico Tomassetti if (property == ObservableProperty.RANGE || property == ObservableProperty.COMMENTED_NODE) { 140d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti return; 141d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 142793f207b1b17498cdb201309ba94c25cb9e4fe20Federico Tomassetti if (property == ObservableProperty.COMMENT) { 14308bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti if (!observedNode.getParentNode().isPresent()) { 14408bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti throw new IllegalStateException(); 14508bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti } 1466149218487aac50fad2256c8723639671747d7bbDanny van Bruggen NodeText nodeText = getOrCreateNodeText(observedNode.getParentNode().get()); 1477a48b21bb866b8d4baf2ce50992c0dfd93f4bc3bFederico Tomassetti if (oldValue == null) { 14808bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti // Find the position of the comment node and put in front of it the comment and a newline 14908bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti int index = nodeText.findChild(observedNode); 15048789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen nodeText.addChild(index, (Comment) newValue); 1516a1ee724c337694ee28b2a09af41c236b880ba66Danny van Bruggen nodeText.addToken(index + 1, eolTokenKind(), Utils.EOL); 1527a48b21bb866b8d4baf2ce50992c0dfd93f4bc3bFederico Tomassetti } else if (newValue == null) { 15308bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti if (oldValue instanceof JavadocComment) { 15448789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen JavadocComment javadocComment = (JavadocComment) oldValue; 15583d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen List<TokenTextElement> matchingTokens = nodeText.getElements().stream().filter(e -> e.isToken(JAVADOC_COMMENT) 15648789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen && ((TokenTextElement) e).getText().equals("/**" + javadocComment.getContent() + "*/")).map(e -> (TokenTextElement) e).collect(Collectors.toList()); 15708bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti if (matchingTokens.size() != 1) { 15808bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti throw new IllegalStateException(); 15908bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti } 16008bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti int index = nodeText.findElement(matchingTokens.get(0)); 16108bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti nodeText.removeElement(index); 1629192b9d17903947058b755b987d1c57a05821e58Danny van Bruggen if (nodeText.getElements().get(index).isNewline()) { 16308bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti nodeText.removeElement(index); 16408bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti } 16508bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti } else { 16608bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti throw new UnsupportedOperationException(); 16708bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti } 168516947a3d4eda478d20dcbb4216d6c52cf11d169Federico Tomassetti } else { 16908bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti if (oldValue instanceof JavadocComment) { 17048789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen JavadocComment oldJavadocComment = (JavadocComment) oldValue; 17183d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen List<TokenTextElement> matchingTokens = nodeText.getElements().stream().filter(e -> e.isToken(JAVADOC_COMMENT) 17248789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen && ((TokenTextElement) e).getText().equals("/**" + oldJavadocComment.getContent() + "*/")).map(e -> (TokenTextElement) e).collect(Collectors.toList()); 17308bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti if (matchingTokens.size() != 1) { 17408bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti throw new IllegalStateException(); 17508bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti } 17648789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen JavadocComment newJavadocComment = (JavadocComment) newValue; 17783d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen nodeText.replace(matchingTokens.get(0), new TokenTextElement(JAVADOC_COMMENT, "/**" + newJavadocComment.getContent() + "*/")); 17808bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti } else { 17908bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti throw new UnsupportedOperationException(); 18008bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti } 18108bd6db12a6e7a8c9eebba117981bbde6399d4ffFederico Tomassetti } 182793f207b1b17498cdb201309ba94c25cb9e4fe20Federico Tomassetti } 1836149218487aac50fad2256c8723639671747d7bbDanny van Bruggen NodeText nodeText = getOrCreateNodeText(observedNode); 184f0f313058c4389cd48052112e3fb906c060be30fFederico Tomassetti 185f0f313058c4389cd48052112e3fb906c060be30fFederico Tomassetti if (nodeText == null) { 186f0f313058c4389cd48052112e3fb906c060be30fFederico Tomassetti throw new NullPointerException(observedNode.getClass().getSimpleName()); 187f0f313058c4389cd48052112e3fb906c060be30fFederico Tomassetti } 188c73ae5d79b62de91209477538b45ab5216fb108eFederico Tomassetti 189c73ae5d79b62de91209477538b45ab5216fb108eFederico Tomassetti new LexicalDifferenceCalculator().calculatePropertyChange(nodeText, observedNode, property, oldValue, newValue); 190d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 191d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 192d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti @Override 19315cb49e74025eb4011187f31735aa4772993fb5dFederico Tomassetti public void concreteListChange(NodeList changedList, ListChangeType type, int index, Node nodeAddedOrRemoved) { 1946149218487aac50fad2256c8723639671747d7bbDanny van Bruggen NodeText nodeText = getOrCreateNodeText(changedList.getParentNodeForChildren()); 195d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti if (type == ListChangeType.REMOVAL) { 19646d1656c7a0b6a5313077495d02f4f98f69be414Danny van Bruggen new LexicalDifferenceCalculator().calculateListRemovalDifference(findNodeListName(changedList), changedList, index).apply(nodeText, changedList.getParentNodeForChildren()); 197d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } else if (type == ListChangeType.ADDITION) { 19848789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen new LexicalDifferenceCalculator().calculateListAdditionDifference(findNodeListName(changedList), changedList, index, nodeAddedOrRemoved).apply(nodeText, changedList.getParentNodeForChildren()); 199d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } else { 200d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti throw new UnsupportedOperationException(); 201d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 202d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 203ca2826ef2cf2a40c0c0d061ee68ba525d2481becFederico Tomassetti 204ca2826ef2cf2a40c0c0d061ee68ba525d2481becFederico Tomassetti @Override 20515cb49e74025eb4011187f31735aa4772993fb5dFederico Tomassetti public void concreteListReplacement(NodeList changedList, int index, Node oldValue, Node newValue) { 2066149218487aac50fad2256c8723639671747d7bbDanny van Bruggen NodeText nodeText = getOrCreateNodeText(changedList.getParentNodeForChildren()); 20746d1656c7a0b6a5313077495d02f4f98f69be414Danny van Bruggen new LexicalDifferenceCalculator().calculateListReplacementDifference(findNodeListName(changedList), changedList, index, newValue).apply(nodeText, changedList.getParentNodeForChildren()); 208ca2826ef2cf2a40c0c0d061ee68ba525d2481becFederico Tomassetti } 209d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti }; 210d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 211d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 2126149218487aac50fad2256c8723639671747d7bbDanny van Bruggen private static void storeInitialText(Node root) { 213198c4d502c00c07c674543bfb1eebedafcf4b193Federico Tomassetti Map<Node, List<JavaToken>> tokensByNode = new IdentityHashMap<>(); 214d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 2158a2ab8b7cc640f95ca1fb10bc0e8534d766e9299Sergey Reshetnik // We go over tokens and find to which nodes they belong. Note that we do not traverse the tokens as they were 2168a2ab8b7cc640f95ca1fb10bc0e8534d766e9299Sergey Reshetnik // on a list but as they were organized in a tree. At each time we select only the branch corresponding to the 2178a2ab8b7cc640f95ca1fb10bc0e8534d766e9299Sergey Reshetnik // range of interest and ignore all other branches 2188324ea0bfcac6db2f2ef2c3d12a84744842aa8adDanny van Bruggen for (JavaToken token : root.getTokenRange().get()) { 2193608acd141a4d3c2105672b4b19b26e4d2badb5cDanny van Bruggen Range tokenRange = token.getRange().orElseThrow(() -> new RuntimeException("Token without range: " + token)); 2202a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik Node owner = findNodeForToken(root, tokenRange); 2212a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik if (owner == null) { 2222a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik throw new RuntimeException("Token without node owning it: " + token); 2232a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik } 224d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti if (!tokensByNode.containsKey(owner)) { 225d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti tokensByNode.put(owner, new LinkedList<>()); 226d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 227d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti tokensByNode.get(owner).add(token); 228d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 229d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 230d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // Now that we know the tokens we use them to create the initial NodeText for each node 231d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti new TreeVisitor() { 232d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti @Override 233d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti public void process(Node node) { 2349a7cda9c8fb4cbb33b5f38daea68921ae201ef31Federico Tomassetti if (!PhantomNodeLogic.isPhantomNode(node)) { 2356149218487aac50fad2256c8723639671747d7bbDanny van Bruggen LexicalPreservingPrinter.storeInitialTextForOneNode(node, tokensByNode.get(node)); 2369a7cda9c8fb4cbb33b5f38daea68921ae201ef31Federico Tomassetti } 237d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 238d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti }.visitBreadthFirst(root); 239d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 240d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 2412a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik private static Node findNodeForToken(Node node, Range tokenRange) { 2422a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik if (PhantomNodeLogic.isPhantomNode(node)) { 2432a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik return null; 2442a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik } 2452a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik if (node.getRange().get().contains(tokenRange)) { 2462a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik for (Node child : node.getChildNodes()) { 2472a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik Node found = findNodeForToken(child, tokenRange); 2482a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik if (found != null) { 2492a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik return found; 2502a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik } 2512a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik } 2522a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik return node; 2532a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik } else { 2542a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik return null; 2552a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik } 2562a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik } 2572a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik 2586149218487aac50fad2256c8723639671747d7bbDanny van Bruggen private static void storeInitialTextForOneNode(Node node, List<JavaToken> nodeTokens) { 259d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti if (nodeTokens == null) { 260d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti nodeTokens = Collections.emptyList(); 261d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 262d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti List<Pair<Range, TextElement>> elements = new LinkedList<>(); 263d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti for (Node child : node.getChildNodes()) { 264f671c0e58dbbe6688926060fee6a9b6cbc9d8f76Federico Tomassetti if (!PhantomNodeLogic.isPhantomNode(child)) { 265516947a3d4eda478d20dcbb4216d6c52cf11d169Federico Tomassetti if (!child.getRange().isPresent()) { 266516947a3d4eda478d20dcbb4216d6c52cf11d169Federico Tomassetti throw new RuntimeException("Range not present on node " + child); 267516947a3d4eda478d20dcbb4216d6c52cf11d169Federico Tomassetti } 2686149218487aac50fad2256c8723639671747d7bbDanny van Bruggen elements.add(new Pair<>(child.getRange().get(), new ChildTextElement(child))); 269f671c0e58dbbe6688926060fee6a9b6cbc9d8f76Federico Tomassetti } 270d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 271d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti for (JavaToken token : nodeTokens) { 2723608acd141a4d3c2105672b4b19b26e4d2badb5cDanny van Bruggen elements.add(new Pair<>(token.getRange().get(), new TokenTextElement(token))); 273d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 27448789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen elements.sort(comparing(e -> e.a.begin)); 2756149218487aac50fad2256c8723639671747d7bbDanny van Bruggen node.setData(NODE_TEXT_DATA, new NodeText(elements.stream().map(p -> p.b).collect(Collectors.toList()))); 276d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 2772a393b02804ec16ccec1877ca3a0289c924cc384Sergey Reshetnik 278d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // 279de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti // Iterators 280de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti // 281de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti 2826149218487aac50fad2256c8723639671747d7bbDanny van Bruggen private static Iterator<TokenTextElement> tokensPreceeding(final Node node) { 283de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti if (!node.getParentNode().isPresent()) { 28446d1656c7a0b6a5313077495d02f4f98f69be414Danny van Bruggen return new TextElementIteratorsFactory.EmptyIterator<>(); 285de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti } 286de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti // There is the awfully painful case of the fake types involved in variable declarators and 287de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti // fields or variable declaration that are, of course, an exception... 288de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti 289de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti NodeText parentNodeText = getOrCreateNodeText(node.getParentNode().get()); 290de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti int index = parentNodeText.tryToFindChild(node); 291de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti if (index == NodeText.NOT_FOUND) { 292de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti if (node.getParentNode().get() instanceof VariableDeclarator) { 293de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti return tokensPreceeding(node.getParentNode().get()); 294de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti } else { 295de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti throw new IllegalArgumentException( 296de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti String.format("I could not find child '%s' in parent '%s'. parentNodeText: %s", 297de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti node, node.getParentNode().get(), parentNodeText)); 298de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti } 299de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti } 300de711eeae58ca512235ce47ed433ed24658f4098Federico Tomassetti 301de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti return new TextElementIteratorsFactory.CascadingIterator<>( 302dea490b3c4b649d5ede5f12dbf6740b816681cf2Federico Tomassetti TextElementIteratorsFactory.partialReverseIterator(parentNodeText, index - 1), 303de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti () -> tokensPreceeding(node.getParentNode().get())); 304de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti } 305de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti 306de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti // 307d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // Printing methods 308d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // 309d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 310d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti /** 311d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * Print a Node into a String, preserving the lexical information. 312d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti */ 3136149218487aac50fad2256c8723639671747d7bbDanny van Bruggen public static String print(Node node) { 314d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti StringWriter writer = new StringWriter(); 315d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti try { 316d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti print(node, writer); 317d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } catch (IOException e) { 318d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti throw new RuntimeException("Unexpected IOException on a StringWriter", e); 319d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 320d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti return writer.toString(); 321d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 322d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 323d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti /** 324d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti * Print a Node into a Writer, preserving the lexical information. 325d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti */ 3266149218487aac50fad2256c8723639671747d7bbDanny van Bruggen public static void print(Node node, Writer writer) throws IOException { 327a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggen if (!node.containsData(NODE_TEXT_DATA)) { 328eeb2701b8cbc85c513d0759d6fa3f2fc41b81ebcFederico Tomassetti getOrCreateNodeText(node); 329d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 330a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggen final NodeText text = node.getData(NODE_TEXT_DATA); 331eeb2701b8cbc85c513d0759d6fa3f2fc41b81ebcFederico Tomassetti writer.append(text.expand()); 332d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 333d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 334d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // 335d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // Methods to handle transformations 336d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // 337d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 33848789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen private static void prettyPrintingTextNode(Node node, NodeText nodeText) { 339de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti if (node instanceof PrimitiveType) { 34048789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen PrimitiveType primitiveType = (PrimitiveType) node; 341dec68cfc83301b4e7ee2bbf1886959d6aa05f85aFederico Tomassetti switch (primitiveType.getType()) { 342521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti case BOOLEAN: 34383d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen nodeText.addToken(BOOLEAN, node.toString()); 344521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti break; 345521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti case CHAR: 34683d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen nodeText.addToken(CHAR, node.toString()); 347521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti break; 348521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti case BYTE: 34983d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen nodeText.addToken(BYTE, node.toString()); 350521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti break; 351521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti case SHORT: 35283d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen nodeText.addToken(SHORT, node.toString()); 353521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti break; 354dec68cfc83301b4e7ee2bbf1886959d6aa05f85aFederico Tomassetti case INT: 35583d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen nodeText.addToken(INT, node.toString()); 356dec68cfc83301b4e7ee2bbf1886959d6aa05f85aFederico Tomassetti break; 357521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti case LONG: 35883d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen nodeText.addToken(LONG, node.toString()); 359521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti break; 360521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti case FLOAT: 36183d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen nodeText.addToken(FLOAT, node.toString()); 362521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti break; 363521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti case DOUBLE: 36483d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen nodeText.addToken(DOUBLE, node.toString()); 365521384346671f17f0f4e6c2af86ba413aad83ef5Federico Tomassetti break; 366dec68cfc83301b4e7ee2bbf1886959d6aa05f85aFederico Tomassetti default: 367dec68cfc83301b4e7ee2bbf1886959d6aa05f85aFederico Tomassetti throw new IllegalArgumentException(); 368dec68cfc83301b4e7ee2bbf1886959d6aa05f85aFederico Tomassetti } 36948789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen return; 370de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti } 371f0f313058c4389cd48052112e3fb906c060be30fFederico Tomassetti if (node instanceof JavadocComment) { 37248789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen nodeText.addToken(JAVADOC_COMMENT, "/**" + ((JavadocComment) node).getContent() + "*/"); 37348789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen return; 374f0f313058c4389cd48052112e3fb906c060be30fFederico Tomassetti } 375de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti 37648789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen interpret(node, ConcreteSyntaxModel.forClass(node.getClass()), nodeText); 37703df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti } 37803df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti 3796149218487aac50fad2256c8723639671747d7bbDanny van Bruggen private static NodeText interpret(Node node, CsmElement csm, NodeText nodeText) { 38003df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti LexicalDifferenceCalculator.CalculatedSyntaxModel calculatedSyntaxModel = new LexicalDifferenceCalculator().calculatedSyntaxModelForNode(csm, node); 38187b56cb1f18252c95d88b979a449f06d55def290Federico Tomassetti 38287db3c76d76680bd1bea566d44ab74b4dca55b71Federico Tomassetti List<TokenTextElement> indentation = findIndentation(node); 38387b56cb1f18252c95d88b979a449f06d55def290Federico Tomassetti 38487db3c76d76680bd1bea566d44ab74b4dca55b71Federico Tomassetti boolean pendingIndentation = false; 38503df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti for (CsmElement element : calculatedSyntaxModel.elements) { 38648789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen if (pendingIndentation && !(element instanceof CsmToken && ((CsmToken) element).isNewLine())) { 38746d1656c7a0b6a5313077495d02f4f98f69be414Danny van Bruggen indentation.forEach(nodeText::addElement); 38887db3c76d76680bd1bea566d44ab74b4dca55b71Federico Tomassetti } 38987db3c76d76680bd1bea566d44ab74b4dca55b71Federico Tomassetti pendingIndentation = false; 39003df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti if (element instanceof LexicalDifferenceCalculator.CsmChild) { 39103df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti nodeText.addChild(((LexicalDifferenceCalculator.CsmChild) element).getChild()); 3929e91a1d76dbfb28f2bc7c32028fe459b073ee861Federico Tomassetti } else if (element instanceof CsmToken) { 3939e91a1d76dbfb28f2bc7c32028fe459b073ee861Federico Tomassetti CsmToken csmToken = (CsmToken) element; 39487db3c76d76680bd1bea566d44ab74b4dca55b71Federico Tomassetti nodeText.addToken(csmToken.getTokenType(), csmToken.getContent(node)); 39587db3c76d76680bd1bea566d44ab74b4dca55b71Federico Tomassetti if (csmToken.isNewLine()) { 39687db3c76d76680bd1bea566d44ab74b4dca55b71Federico Tomassetti pendingIndentation = true; 39787db3c76d76680bd1bea566d44ab74b4dca55b71Federico Tomassetti } 3989e91a1d76dbfb28f2bc7c32028fe459b073ee861Federico Tomassetti } else if (element instanceof CsmMix) { 39948789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen CsmMix csmMix = (CsmMix) element; 4009e91a1d76dbfb28f2bc7c32028fe459b073ee861Federico Tomassetti csmMix.getElements().forEach(e -> interpret(node, e, nodeText)); 40103df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti } else { 40203df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti throw new UnsupportedOperationException(element.getClass().getSimpleName()); 40303df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti } 40403df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti } 4057e85803c3a66d340e1cbdbc6c98081104ad412c3Federico Tomassetti // Array brackets are a pain... we do not have a way to represent them explicitly in the AST 4067e85803c3a66d340e1cbdbc6c98081104ad412c3Federico Tomassetti // so they have to be handled in a special way 4077e85803c3a66d340e1cbdbc6c98081104ad412c3Federico Tomassetti if (node instanceof VariableDeclarator) { 4089a228915f61892f6ba99fcf1e42e42e1e2622a00Danny van Bruggen VariableDeclarator variableDeclarator = (VariableDeclarator) node; 40948789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen variableDeclarator.getParentNode().ifPresent(parent -> 41048789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen ((NodeWithVariables<?>) parent).getMaximumCommonType().ifPresent(mct -> { 41148789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen int extraArrayLevels = variableDeclarator.getType().getArrayLevel() - mct.getArrayLevel(); 41248789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen for (int i = 0; i < extraArrayLevels; i++) { 41348789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen nodeText.addElement(new TokenTextElement(LBRACKET)); 41448789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen nodeText.addElement(new TokenTextElement(RBRACKET)); 41548789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen } 41648789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen }) 41748789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen ); 4187e85803c3a66d340e1cbdbc6c98081104ad412c3Federico Tomassetti } 41903df427399267338f87748888d1f921bb9a1c0daFederico Tomassetti return nodeText; 420d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 421d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 422d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // Visible for testing 4236149218487aac50fad2256c8723639671747d7bbDanny van Bruggen static NodeText getOrCreateNodeText(Node node) { 424a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggen if (!node.containsData(NODE_TEXT_DATA)) { 4256149218487aac50fad2256c8723639671747d7bbDanny van Bruggen NodeText nodeText = new NodeText(); 426a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggen node.setData(NODE_TEXT_DATA, nodeText); 427fab5a9b624d0748ef3e924573f55386010a2694eFederico Tomassetti prettyPrintingTextNode(node, nodeText); 428d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 429a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggen return node.getData(NODE_TEXT_DATA); 430d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 431d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 4324002ee095f065fb93ce3ad4355bc5fb7b63dafaaFederico Tomassetti // Visible for testing 4336149218487aac50fad2256c8723639671747d7bbDanny van Bruggen static List<TokenTextElement> findIndentation(Node node) { 434152592f7c8110f04ea6ca6e6c6d57360fd40ee16Federico Tomassetti List<TokenTextElement> followingNewlines = new LinkedList<>(); 435de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti Iterator<TokenTextElement> it = tokensPreceeding(node); 436de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti while (it.hasNext()) { 437de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti TokenTextElement tte = it.next(); 43883d60da8afce695e7356af94c549e30ab585448cDanny van Bruggen if (tte.getTokenKind() == SINGLE_LINE_COMMENT 4399192b9d17903947058b755b987d1c57a05821e58Danny van Bruggen || tte.isNewline()) { 440152592f7c8110f04ea6ca6e6c6d57360fd40ee16Federico Tomassetti break; 441152592f7c8110f04ea6ca6e6c6d57360fd40ee16Federico Tomassetti } else { 442152592f7c8110f04ea6ca6e6c6d57360fd40ee16Federico Tomassetti followingNewlines.add(tte); 443152592f7c8110f04ea6ca6e6c6d57360fd40ee16Federico Tomassetti } 444152592f7c8110f04ea6ca6e6c6d57360fd40ee16Federico Tomassetti } 445152592f7c8110f04ea6ca6e6c6d57360fd40ee16Federico Tomassetti Collections.reverse(followingNewlines); 44648789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen for (int i = 0; i < followingNewlines.size(); i++) { 447152592f7c8110f04ea6ca6e6c6d57360fd40ee16Federico Tomassetti if (!followingNewlines.get(i).isSpaceOrTab()) { 448152592f7c8110f04ea6ca6e6c6d57360fd40ee16Federico Tomassetti return followingNewlines.subList(0, i); 449de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti } 450de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti } 451152592f7c8110f04ea6ca6e6c6d57360fd40ee16Federico Tomassetti return followingNewlines; 452de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti } 453de11a2ae80166defacd1f9c51b77f52be4b53eb1Federico Tomassetti 454d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // 455d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // Helper methods 456d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti // 457d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti 458d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti private static boolean isReturningOptionalNodeList(Method m) { 459d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti if (!m.getReturnType().getCanonicalName().equals(Optional.class.getCanonicalName())) { 460d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti return false; 461d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti } 462d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti if (!(m.getGenericReturnType() instanceof ParameterizedType)) { 463d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti return false; 464d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti } 465d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti ParameterizedType parameterizedType = (ParameterizedType) m.getGenericReturnType(); 466d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti java.lang.reflect.Type optionalArgument = parameterizedType.getActualTypeArguments()[0]; 467a86af3ac653113b302dcfa9facc6c67159fabf1bDanny van Bruggen return (optionalArgument.getTypeName().startsWith(NodeList.class.getCanonicalName())); 468d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti } 469d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti 470d533531b8d35f2d899f9e826e1993a16226e7580Federico Tomassetti private static ObservableProperty findNodeListName(NodeList nodeList) { 471d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti Node parent = nodeList.getParentNodeForChildren(); 472d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti for (Method m : parent.getClass().getMethods()) { 473d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti if (m.getParameterCount() == 0 && m.getReturnType().getCanonicalName().equals(NodeList.class.getCanonicalName())) { 474d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti try { 4751fa1ea21c227ad0cf06a3d12638f63061186f02fFederico Tomassetti Object raw = m.invoke(parent); 4761fa1ea21c227ad0cf06a3d12638f63061186f02fFederico Tomassetti if (!(raw instanceof NodeList)) { 4771fa1ea21c227ad0cf06a3d12638f63061186f02fFederico Tomassetti throw new IllegalStateException("Expected NodeList, found " + raw.getClass().getCanonicalName()); 4781fa1ea21c227ad0cf06a3d12638f63061186f02fFederico Tomassetti } 47948789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen NodeList result = (NodeList) raw; 480d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti if (result == nodeList) { 481d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti String name = m.getName(); 482d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti if (name.startsWith("get")) { 483d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti name = name.substring("get".length()); 484d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 485d4db09d04aca5a9df4278652d55d680c4a826efcFederico Tomassetti return ObservableProperty.fromCamelCaseName(decapitalize(name)); 486d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 487d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } catch (IllegalAccessException | InvocationTargetException e) { 488d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti throw new RuntimeException(e); 489d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 490d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti } else if (m.getParameterCount() == 0 && isReturningOptionalNodeList(m)) { 491d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti try { 49248789abfd3738d660abb6f8cd758a40c57379597Danny van Bruggen Optional<NodeList<?>> raw = (Optional<NodeList<?>>) m.invoke(parent); 493d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti if (raw.isPresent() && raw.get() == nodeList) { 494d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti String name = m.getName(); 495d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti if (name.startsWith("get")) { 496d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti name = name.substring("get".length()); 497d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti } 498d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti return ObservableProperty.fromCamelCaseName(decapitalize(name)); 499d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti } 500d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti } catch (IllegalAccessException | InvocationTargetException e) { 501d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti throw new RuntimeException(e); 502d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti } 503d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 504d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 505d6aebf46978852659303ad0fe057e1cddd9e391dFederico Tomassetti throw new IllegalArgumentException("Cannot find list name of NodeList of size " + nodeList.size()); 506d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti } 507d25e79e2c567cc2fc312fabe15fa35be886f31b2Federico Tomassetti} 508