Home | History | Annotate | Download | only in lexicalpreservation
      1 /*
      2  * Copyright (C) 2007-2010 Jlio 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 
     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      */
     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