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