1 /* 2 * Copyright (C) 2007-2010 Júlio Vilmar Gesser. 3 * Copyright (C) 2011, 2013-2016 The JavaParser Team. 4 * 5 * This file is part of JavaParser. 6 * 7 * JavaParser can be used either under the terms of 8 * a) the GNU Lesser General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * b) the terms of the Apache License 12 * 13 * You should have received a copy of both licenses in LICENCE.LGPL and 14 * LICENCE.APACHE. Please refer to those files for details. 15 * 16 * JavaParser is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU Lesser General Public License for more details. 20 */ 21 22 package com.github.javaparser.printer.lexicalpreservation; 23 24 import com.github.javaparser.ast.Node; 25 import com.github.javaparser.ast.observer.AstObserver; 26 import com.github.javaparser.ast.observer.AstObserverAdapter; 27 import com.github.javaparser.ast.type.UnknownType; 28 29 import java.util.IdentityHashMap; 30 import java.util.Map; 31 32 import static java.util.Collections.synchronizedMap; 33 34 /** 35 * We want to recognize and ignore "phantom" nodes, like the fake type of variable in FieldDeclaration 36 */ 37 class PhantomNodeLogic { 38 39 private static final int LEVELS_TO_EXPLORE = 3; 40 41 private static final Map<Node, Boolean> isPhantomNodeCache = synchronizedMap(new IdentityHashMap<>()); 42 43 private static final AstObserver cacheCleaner = new AstObserverAdapter() { 44 @Override 45 public void parentChange(Node observedNode, Node previousParent, Node newParent) { 46 isPhantomNodeCache.remove(observedNode); 47 } 48 }; 49 isPhantomNode(Node node)50 static boolean isPhantomNode(Node node) { 51 if (isPhantomNodeCache.containsKey(node)) { 52 return isPhantomNodeCache.get(node); 53 } else { 54 if (node instanceof UnknownType) { 55 return true; 56 } 57 boolean res = (node.getParentNode().isPresent() && 58 !node.getParentNode().get().getRange().get().contains(node.getRange().get()) 59 || inPhantomNode(node, LEVELS_TO_EXPLORE)); 60 isPhantomNodeCache.put(node, res); 61 node.register(cacheCleaner); 62 return res; 63 } 64 } 65 66 /** 67 * A node contained in a phantom node is also a phantom node. We limit how many levels up we check just for performance reasons. 68 */ inPhantomNode(Node node, int levels)69 private static boolean inPhantomNode(Node node, int levels) { 70 return node.getParentNode().isPresent() && 71 (isPhantomNode(node.getParentNode().get()) 72 || inPhantomNode(node.getParentNode().get(), levels - 1)); 73 } 74 } 75