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 
     26 import java.util.LinkedList;
     27 import java.util.List;
     28 
     29 /**
     30  * This contains the lexical information for a single node.
     31  * It is basically a list of tokens and children.
     32  */
     33 class NodeText {
     34     private final List<TextElement> elements;
     35 
     36     public static final int NOT_FOUND = -1;
     37 
     38     enum Option {
     39         REMOVE_SPACE_IMMEDIATELY_AFTER,
     40         EXCLUDE_START,
     41         EXCLUDE_END
     42     }
     43 
     44     //
     45     // Constructors
     46     //
     47 
     48     NodeText(List<TextElement> elements) {
     49         this.elements = elements;
     50     }
     51 
     52     /**
     53      * Initialize with an empty list of elements.
     54      */
     55     NodeText() {
     56         this(new LinkedList<>());
     57     }
     58 
     59     //
     60     // Adding elements
     61     //
     62 
     63     /**
     64      * Add an element at the end.
     65      */
     66     void addElement(TextElement nodeTextElement) {
     67         this.elements.add(nodeTextElement);
     68     }
     69 
     70     /**
     71      * Add an element at the given position.
     72      */
     73     void addElement(int index, TextElement nodeTextElement) {
     74         this.elements.add(index, nodeTextElement);
     75     }
     76 
     77     void addChild(Node child) {
     78         addElement(new ChildTextElement(child));
     79     }
     80 
     81     void addChild(int index, Node child) {
     82         addElement(index, new ChildTextElement(child));
     83     }
     84 
     85     void addToken(int tokenKind, String text) {
     86         elements.add(new TokenTextElement(tokenKind, text));
     87     }
     88 
     89     void addToken(int index, int tokenKind, String text) {
     90         elements.add(index, new TokenTextElement(tokenKind, text));
     91     }
     92 
     93     //
     94     // Finding elements
     95     //
     96 
     97     int findElement(TextElementMatcher matcher) {
     98         return findElement(matcher, 0);
     99     }
    100 
    101     int findElement(TextElementMatcher matcher, int from) {
    102         int res = tryToFindElement(matcher, from);
    103         if (res == NOT_FOUND) {
    104             throw new IllegalArgumentException(
    105                     String.format("I could not find child '%s' from position %d. Elements: %s",
    106                             matcher, from, elements));
    107         } else {
    108             return res;
    109         }
    110     }
    111 
    112     int tryToFindElement(TextElementMatcher matcher, int from) {
    113         for (int i=from; i<elements.size(); i++) {
    114             TextElement element = elements.get(i);
    115             if (matcher.match(element)) {
    116                 return i;
    117             }
    118         }
    119         return NOT_FOUND;
    120     }
    121 
    122     int findChild(Node child) {
    123         return findChild(child, 0);
    124     }
    125 
    126     int findChild(Node child, int from) {
    127         return findElement(TextElementMatchers.byNode(child), from);
    128     }
    129 
    130     int tryToFindChild(Node child) {
    131         return tryToFindChild(child, 0);
    132     }
    133 
    134     int tryToFindChild(Node child, int from) {
    135         return tryToFindElement(TextElementMatchers.byNode(child), from);
    136     }
    137 
    138     //
    139     // Removing single elements
    140     //
    141 
    142     void remove(TextElementMatcher matcher) {
    143         elements.removeIf(matcher::match);
    144     }
    145 
    146     public void remove(TextElementMatcher matcher, boolean potentiallyFollowingWhitespace) {
    147         int i=0;
    148         for (TextElement e : elements) {
    149             if (matcher.match(e)) {
    150                 elements.remove(e);
    151                 if (potentiallyFollowingWhitespace) {
    152                     if (i < elements.size()) {
    153                         if (elements.get(i).isWhiteSpace()) {
    154                             elements.remove(i);
    155                         }
    156                     } else {
    157                         throw new UnsupportedOperationException();
    158                     }
    159                 }
    160                 return;
    161             }
    162         }
    163         throw new IllegalArgumentException();
    164     }
    165 
    166     //
    167     // Removing sequences
    168     //
    169 
    170     void removeElement(int index) {
    171         elements.remove(index);
    172     }
    173 
    174     //
    175     // Replacing elements
    176     //
    177 
    178     void replace(TextElementMatcher position, TextElement newElement) {
    179         int index = findElement(position, 0);
    180         elements.remove(index);
    181         elements.add(index, newElement);
    182     }
    183 
    184     //
    185     // Other methods
    186     //
    187 
    188     /**
    189      * Generate the corresponding string.
    190      */
    191     String expand() {
    192         StringBuffer sb = new StringBuffer();
    193 
    194         elements.forEach(e -> sb.append(e.expand()));
    195         return sb.toString();
    196     }
    197 
    198     // Visible for testing
    199     int numberOfElements() {
    200         return elements.size();
    201     }
    202 
    203     // Visible for testing
    204     TextElement getTextElement(int index) {
    205         return elements.get(index);
    206     }
    207 
    208     // Visible for testing
    209     List<TextElement> getElements() {
    210         return elements;
    211     }
    212 
    213     @Override
    214     public String toString() {
    215         return "NodeText{" + elements + '}';
    216     }
    217 }
    218