1 | /* |
---|---|
2 | * Copyright (C) 2007-2010 JĂșlio Vilmar Gesser. |
3 | * Copyright (C) 2011, 2013-2020 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.JavaToken; |
25 | import com.github.javaparser.TokenRange; |
26 | import com.github.javaparser.TokenTypes; |
27 | import com.github.javaparser.ast.Node; |
28 | import com.github.javaparser.printer.concretesyntaxmodel.CsmToken; |
29 | |
30 | import java.util.Iterator; |
31 | import java.util.List; |
32 | import java.util.Optional; |
33 | import java.util.function.Function; |
34 | import java.util.stream.Collectors; |
35 | import java.util.stream.IntStream; |
36 | |
37 | /** |
38 | * This class represents a group of {@link Removed} elements. |
39 | * The {@link Removed} elements are ideally consecutive for the methods in this class to work correctly. |
40 | * |
41 | * This class consists of methods that calculate information to better handle the difference application for the |
42 | * containing {@link Removed} elements. |
43 | * |
44 | * @see Iterable |
45 | * |
46 | * @author ThLeu |
47 | */ |
48 | final class RemovedGroup implements Iterable<Removed> { |
49 | |
50 | private final Integer firstElementIndex; |
51 | private final List<Removed> removedList; |
52 | |
53 | private boolean isProcessed = false; |
54 | |
55 | private RemovedGroup(Integer firstElementIndex, List<Removed> removedList) { |
56 | if (firstElementIndex == null) { |
57 | throw new IllegalArgumentException("firstElementIndex should not be null"); |
58 | } |
59 | |
60 | if (removedList == null || removedList.isEmpty()) { |
61 | throw new IllegalArgumentException("removedList should not be null or empty"); |
62 | } |
63 | |
64 | this.firstElementIndex = firstElementIndex; |
65 | this.removedList = removedList; |
66 | } |
67 | |
68 | /** |
69 | * Factory method to create a RemovedGroup which consists of consecutive Removed elements |
70 | * |
71 | * @param firstElementIndex the difference index at which the RemovedGroup starts |
72 | * @param removedList list of the consecutive Removed elements |
73 | * @return a RemovedGroup object |
74 | * @throws IllegalArgumentException if the firstElementIndex is null or the removedList is empty or null |
75 | */ |
76 | public static RemovedGroup of(Integer firstElementIndex, List<Removed> removedList) { |
77 | return new RemovedGroup(firstElementIndex, removedList); |
78 | } |
79 | |
80 | /** |
81 | * Marks the RemovedGroup as processed which indicates that it should not be processed again |
82 | */ |
83 | final void processed() { |
84 | isProcessed = true; |
85 | } |
86 | |
87 | /** |
88 | * Returns whether the RemovedGroup was already processed and should not be processed again |
89 | * |
90 | * @return wheter the RemovedGroup was already processed |
91 | */ |
92 | final boolean isProcessed() { |
93 | return isProcessed; |
94 | } |
95 | |
96 | private List<Integer> getIndicesBeingRemoved() { |
97 | return IntStream.range(firstElementIndex, firstElementIndex + removedList.size()) |
98 | .boxed() |
99 | .collect(Collectors.toList()); |
100 | } |
101 | |
102 | /** |
103 | * Returns the difference index of the last element being removed with this RemovedGroup |
104 | * |
105 | * @return the last difference incex of this RemovedGroup |
106 | */ |
107 | final Integer getLastElementIndex() { |
108 | List<Integer> indicesBeingRemoved = getIndicesBeingRemoved(); |
109 | return indicesBeingRemoved.get(indicesBeingRemoved.size() - 1); |
110 | } |
111 | |
112 | /** |
113 | * Returns the first element of this RemovedGroup |
114 | * |
115 | * @return the first element of this RemovedGroup |
116 | */ |
117 | final Removed getFirstElement() { |
118 | return removedList.get(0); |
119 | } |
120 | |
121 | /** |
122 | * Returns the last element of this RemovedGroup |
123 | * |
124 | * @return the last element of this RemovedGroup |
125 | */ |
126 | final Removed getLastElement() { |
127 | return removedList.get(removedList.size() - 1); |
128 | } |
129 | |
130 | /** |
131 | * Returns true if the RemovedGroup equates to a complete line |
132 | * This is the case if there are only spaces and tabs left on the line besides the Removed elements. |
133 | * <br> |
134 | * Example: |
135 | * <pre> |
136 | * " [Removed] [EOL]" -> this would be a complete line, regardless of spaces or tabs before or after the [Removed] element |
137 | * " [Removed] void [EOL]" -> this would not be a complete line because of the "void" |
138 | * " public [Removed] [EOL]" -> this would not be a complete line because of the "public" |
139 | * </pre> |
140 | * |
141 | * @return true if the RemovedGroup equates to a complete line |
142 | */ |
143 | final boolean isACompleteLine() { |
144 | return hasOnlyWhitespace(getFirstElement(), hasOnlyWhitespaceInFrontFunction) |
145 | && hasOnlyWhitespace(getLastElement(), hasOnlyWhitespaceBehindFunction); |
146 | } |
147 | |
148 | private final Function<JavaToken, Boolean> hasOnlyWhitespaceJavaTokenInFrontFunction = begin -> hasOnlyWhiteSpaceForTokenFunction(begin, token -> token.getPreviousToken()); |
149 | private final Function<JavaToken, Boolean> hasOnlyWhitespaceJavaTokenBehindFunction = end -> hasOnlyWhiteSpaceForTokenFunction(end, token -> token.getNextToken()); |
150 | private final Function<TokenRange, Boolean> hasOnlyWhitespaceInFrontFunction = tokenRange -> hasOnlyWhitespaceJavaTokenInFrontFunction.apply(tokenRange.getBegin()); |
151 | private final Function<TokenRange, Boolean> hasOnlyWhitespaceBehindFunction = tokenRange -> hasOnlyWhitespaceJavaTokenBehindFunction.apply(tokenRange.getEnd()); |
152 | |
153 | private boolean hasOnlyWhitespace(Removed startElement, Function<TokenRange, Boolean> hasOnlyWhitespaceFunction) { |
154 | boolean hasOnlyWhitespace = false; |
155 | if (startElement.isChild()) { |
156 | LexicalDifferenceCalculator.CsmChild csmChild = (LexicalDifferenceCalculator.CsmChild) startElement.getElement(); |
157 | Node child = csmChild.getChild(); |
158 | |
159 | Optional<TokenRange> tokenRange = child.getTokenRange(); |
160 | if (tokenRange.isPresent()) { |
161 | hasOnlyWhitespace = hasOnlyWhitespaceFunction.apply(tokenRange.get()); |
162 | } |
163 | } else if (startElement.isToken()) { |
164 | CsmToken token = (CsmToken) startElement.getElement(); |
165 | if (TokenTypes.isEndOfLineToken(token.getTokenType())) { |
166 | hasOnlyWhitespace = true; |
167 | } |
168 | } |
169 | return hasOnlyWhitespace; |
170 | } |
171 | |
172 | private boolean hasOnlyWhiteSpaceForTokenFunction(JavaToken token, Function<JavaToken, Optional<JavaToken>> tokenFunction) { |
173 | Optional<JavaToken> tokenResult = tokenFunction.apply(token); |
174 | |
175 | if (tokenResult.isPresent()) { |
176 | if (TokenTypes.isWhitespaceButNotEndOfLine(tokenResult.get().getKind())) { |
177 | return hasOnlyWhiteSpaceForTokenFunction(tokenResult.get(), tokenFunction); |
178 | } else if (TokenTypes.isEndOfLineToken(tokenResult.get().getKind())) { |
179 | return true; |
180 | } else { |
181 | return false; |
182 | } |
183 | } |
184 | |
185 | return true; |
186 | } |
187 | |
188 | /** |
189 | * Returns the indentation in front of this RemovedGroup if possible. |
190 | * If there is something else than whitespace in front, Optional.empty() is returned. |
191 | * |
192 | * @return the indentation in front of this RemovedGroup or Optional.empty() |
193 | */ |
194 | final Optional<Integer> getIndentation() { |
195 | Removed firstElement = getFirstElement(); |
196 | |
197 | int indentation = 0; |
198 | if (firstElement.isChild()) { |
199 | LexicalDifferenceCalculator.CsmChild csmChild = (LexicalDifferenceCalculator.CsmChild) firstElement.getElement(); |
200 | Node child = csmChild.getChild(); |
201 | |
202 | Optional<TokenRange> tokenRange = child.getTokenRange(); |
203 | if (tokenRange.isPresent()) { |
204 | JavaToken begin = tokenRange.get().getBegin(); |
205 | |
206 | if (hasOnlyWhitespaceJavaTokenInFrontFunction.apply(begin)) { |
207 | Optional<JavaToken> previousToken = begin.getPreviousToken(); |
208 | |
209 | while(previousToken.isPresent() && (TokenTypes.isWhitespaceButNotEndOfLine(previousToken.get().getKind()))) { |
210 | indentation++; |
211 | |
212 | previousToken = previousToken.get().getPreviousToken(); |
213 | } |
214 | |
215 | if (previousToken.isPresent()) { |
216 | if (TokenTypes.isEndOfLineToken(previousToken.get().getKind())) { |
217 | return Optional.of(Integer.valueOf(indentation)); |
218 | } else { |
219 | return Optional.empty(); |
220 | } |
221 | } else { |
222 | return Optional.of(Integer.valueOf(indentation)); |
223 | } |
224 | } |
225 | } |
226 | } |
227 | |
228 | return Optional.empty(); |
229 | } |
230 | |
231 | @Override |
232 | public final Iterator<Removed> iterator() { |
233 | return new Iterator<Removed>() { |
234 | private int currentIndex = 0; |
235 | |
236 | @Override |
237 | public boolean hasNext() { |
238 | return currentIndex < removedList.size() && removedList.get(currentIndex) != null; |
239 | } |
240 | |
241 | @Override |
242 | public Removed next() { |
243 | return removedList.get(currentIndex++); |
244 | } |
245 | |
246 | }; |
247 | } |
248 | } |
249 |
Members