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