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