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; |
23 | |
24 | import com.github.javaparser.ParseResult.PostProcessor; |
25 | import com.github.javaparser.Providers.PreProcessor; |
26 | import com.github.javaparser.UnicodeEscapeProcessingProvider.PositionMapping; |
27 | import com.github.javaparser.ast.CompilationUnit; |
28 | import com.github.javaparser.ast.Node; |
29 | import com.github.javaparser.ast.validator.*; |
30 | import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter; |
31 | import com.github.javaparser.resolution.SymbolResolver; |
32 | import com.github.javaparser.utils.LineSeparator; |
33 | import com.github.javaparser.version.Java10PostProcessor; |
34 | import com.github.javaparser.version.Java11PostProcessor; |
35 | import com.github.javaparser.version.Java12PostProcessor; |
36 | import com.github.javaparser.version.Java13PostProcessor; |
37 | import com.github.javaparser.version.Java14PostProcessor; |
38 | |
39 | import java.nio.charset.Charset; |
40 | import java.util.ArrayList; |
41 | import java.util.List; |
42 | import java.util.Optional; |
43 | |
44 | import static com.github.javaparser.ParserConfiguration.LanguageLevel.JAVA_8; |
45 | |
46 | /** |
47 | * The configuration that is used by the parser. |
48 | * Note that this can be changed even when reusing the same JavaParser instance. |
49 | * It will pick up the changes. |
50 | */ |
51 | public class ParserConfiguration { |
52 | |
53 | public enum LanguageLevel { |
54 | /** |
55 | * Java 1.0 |
56 | */ |
57 | JAVA_1_0(new Java1_0Validator(), null), |
58 | /** |
59 | * Java 1.1 |
60 | */ |
61 | JAVA_1_1(new Java1_1Validator(), null), |
62 | /** |
63 | * Java 1.2 |
64 | */ |
65 | JAVA_1_2(new Java1_2Validator(), null), |
66 | /** |
67 | * Java 1.3 |
68 | */ |
69 | JAVA_1_3(new Java1_3Validator(), null), |
70 | /** |
71 | * Java 1.4 |
72 | */ |
73 | JAVA_1_4(new Java1_4Validator(), null), |
74 | /** |
75 | * Java 5 |
76 | */ |
77 | JAVA_5(new Java5Validator(), null), |
78 | /** |
79 | * Java 6 |
80 | */ |
81 | JAVA_6(new Java6Validator(), null), |
82 | /** |
83 | * Java 7 |
84 | */ |
85 | JAVA_7(new Java7Validator(), null), |
86 | /** |
87 | * Java 8 |
88 | */ |
89 | JAVA_8(new Java8Validator(), null), |
90 | /** |
91 | * Java 9 |
92 | */ |
93 | JAVA_9(new Java9Validator(), null), |
94 | /** |
95 | * Java 10 |
96 | */ |
97 | JAVA_10(new Java10Validator(), new Java10PostProcessor()), |
98 | /** |
99 | * Java 11 |
100 | */ |
101 | JAVA_11(new Java11Validator(), new Java11PostProcessor()), |
102 | /** |
103 | * Java 12 |
104 | */ |
105 | JAVA_12(new Java12Validator(), new Java12PostProcessor()), |
106 | /** |
107 | * Java 13 |
108 | */ |
109 | JAVA_13(new Java13Validator(), new Java13PostProcessor()), |
110 | /** |
111 | * Java 14 |
112 | */ |
113 | JAVA_14(new Java14Validator(), new Java14PostProcessor()); |
114 | |
115 | /** |
116 | * Does no post processing or validation. Only for people wanting the fastest parsing. |
117 | */ |
118 | public static LanguageLevel RAW = null; |
119 | /** |
120 | * The most used Java version. |
121 | */ |
122 | public static LanguageLevel POPULAR = JAVA_8; |
123 | /** |
124 | * The latest Java version that is available. |
125 | */ |
126 | public static LanguageLevel CURRENT = JAVA_13; |
127 | /** |
128 | * The newest Java features supported. |
129 | */ |
130 | public static LanguageLevel BLEEDING_EDGE = JAVA_14; |
131 | |
132 | final Validator validator; |
133 | final ParseResult.PostProcessor postProcessor; |
134 | |
135 | LanguageLevel(Validator validator, ParseResult.PostProcessor postProcessor) { |
136 | this.validator = validator; |
137 | this.postProcessor = postProcessor; |
138 | } |
139 | } |
140 | |
141 | |
142 | |
143 | // TODO: Add a configurable option e.g. setDesiredLineEnding(...) to replace/swap out existing line endings |
144 | private boolean detectOriginalLineSeparator = true; |
145 | private boolean storeTokens = true; |
146 | private boolean attributeComments = true; |
147 | private boolean doNotAssignCommentsPrecedingEmptyLines = true; |
148 | private boolean ignoreAnnotationsWhenAttributingComments = false; |
149 | private boolean lexicalPreservationEnabled = false; |
150 | private boolean preprocessUnicodeEscapes = false; |
151 | private SymbolResolver symbolResolver = null; |
152 | private int tabSize = 1; |
153 | private LanguageLevel languageLevel = JAVA_8; |
154 | private Charset characterEncoding = Providers.UTF8; |
155 | |
156 | private final List<Providers.PreProcessor> preProcessors = new ArrayList<>(); |
157 | private final List<ParseResult.PostProcessor> postProcessors = new ArrayList<>(); |
158 | |
159 | public ParserConfiguration() { |
160 | |
161 | class UnicodeEscapeProcessor implements PreProcessor, PostProcessor { |
162 | private UnicodeEscapeProcessingProvider _unicodeDecoder; |
163 | |
164 | @Override |
165 | public Provider process(Provider innerProvider) { |
166 | if (isPreprocessUnicodeEscapes()) { |
167 | _unicodeDecoder = new UnicodeEscapeProcessingProvider(innerProvider); |
168 | return _unicodeDecoder; |
169 | } |
170 | return innerProvider; |
171 | } |
172 | |
173 | @Override |
174 | public void process(ParseResult<? extends Node> result, |
175 | ParserConfiguration configuration) { |
176 | if (isPreprocessUnicodeEscapes()) { |
177 | result.getResult().ifPresent( |
178 | root -> { |
179 | PositionMapping mapping = _unicodeDecoder.getPositionMapping(); |
180 | if (!mapping.isEmpty()) { |
181 | root.walk( |
182 | node -> node.getRange().ifPresent( |
183 | range -> node.setRange(mapping.transform(range)))); |
184 | } |
185 | } |
186 | ); |
187 | } |
188 | } |
189 | } |
190 | |
191 | class LineEndingProcessor implements PreProcessor, PostProcessor { |
192 | private LineEndingProcessingProvider _lineEndingProcessingProvider; |
193 | |
194 | @Override |
195 | public Provider process(Provider innerProvider) { |
196 | if (isDetectOriginalLineSeparator()) { |
197 | _lineEndingProcessingProvider = new LineEndingProcessingProvider(innerProvider); |
198 | return _lineEndingProcessingProvider; |
199 | } |
200 | return innerProvider; |
201 | } |
202 | |
203 | @Override |
204 | public void process(ParseResult<? extends Node> result, ParserConfiguration configuration) { |
205 | if (isDetectOriginalLineSeparator()) { |
206 | result.getResult().ifPresent( |
207 | rootNode -> { |
208 | LineSeparator detectedLineSeparator = _lineEndingProcessingProvider.getDetectedLineEnding(); |
209 | |
210 | // Set the line ending on the root node |
211 | rootNode.setData(Node.LINE_SEPARATOR_KEY, detectedLineSeparator); |
212 | |
213 | // // Set the line ending on all children of the root node -- FIXME: Should ignore """textblocks""" |
214 | // rootNode.findAll(Node.class) |
215 | // .forEach(node -> node.setData(Node.LINE_SEPARATOR_KEY, detectedLineSeparator)); |
216 | } |
217 | ); |
218 | } |
219 | } |
220 | } |
221 | |
222 | UnicodeEscapeProcessor unicodeProcessor = new UnicodeEscapeProcessor(); |
223 | preProcessors.add(unicodeProcessor); |
224 | postProcessors.add(unicodeProcessor); |
225 | |
226 | LineEndingProcessor lineEndingProcessor = new LineEndingProcessor(); |
227 | preProcessors.add(lineEndingProcessor); |
228 | postProcessors.add(lineEndingProcessor); |
229 | |
230 | |
231 | postProcessors.add((result, configuration) -> { |
232 | if (configuration.isAttributeComments()) { |
233 | result.ifSuccessful(resultNode -> result |
234 | .getCommentsCollection().ifPresent(comments -> |
235 | new CommentsInserter(configuration).insertComments(resultNode, comments.copy().getComments()))); |
236 | } |
237 | }); |
238 | postProcessors.add((result, configuration) -> { |
239 | LanguageLevel languageLevel = getLanguageLevel(); |
240 | if (languageLevel != null) { |
241 | if (languageLevel.postProcessor != null) { |
242 | languageLevel.postProcessor.process(result, configuration); |
243 | } |
244 | if (languageLevel.validator != null) { |
245 | languageLevel.validator.accept(result.getResult().get(), new ProblemReporter(newProblem -> result.getProblems().add(newProblem))); |
246 | } |
247 | } |
248 | }); |
249 | postProcessors.add((result, configuration) -> configuration.getSymbolResolver().ifPresent(symbolResolver -> |
250 | result.ifSuccessful(resultNode -> { |
251 | if (resultNode instanceof CompilationUnit) { |
252 | resultNode.setData(Node.SYMBOL_RESOLVER_KEY, symbolResolver); |
253 | } |
254 | }) |
255 | )); |
256 | postProcessors.add((result, configuration) -> { |
257 | if (configuration.isLexicalPreservationEnabled()) { |
258 | result.ifSuccessful(LexicalPreservingPrinter::setup); |
259 | } |
260 | }); |
261 | } |
262 | |
263 | public boolean isAttributeComments() { |
264 | return attributeComments; |
265 | } |
266 | |
267 | /** |
268 | * Whether to run CommentsInserter, which will put the comments that were found in the source code into the comment |
269 | * and javadoc fields of the nodes it thinks they refer to. |
270 | */ |
271 | public ParserConfiguration setAttributeComments(boolean attributeComments) { |
272 | this.attributeComments = attributeComments; |
273 | return this; |
274 | } |
275 | |
276 | public boolean isDoNotAssignCommentsPrecedingEmptyLines() { |
277 | return doNotAssignCommentsPrecedingEmptyLines; |
278 | } |
279 | |
280 | public ParserConfiguration setDoNotAssignCommentsPrecedingEmptyLines(boolean doNotAssignCommentsPrecedingEmptyLines) { |
281 | this.doNotAssignCommentsPrecedingEmptyLines = doNotAssignCommentsPrecedingEmptyLines; |
282 | return this; |
283 | } |
284 | |
285 | public boolean isIgnoreAnnotationsWhenAttributingComments() { |
286 | return ignoreAnnotationsWhenAttributingComments; |
287 | } |
288 | |
289 | public ParserConfiguration setIgnoreAnnotationsWhenAttributingComments(boolean ignoreAnnotationsWhenAttributingComments) { |
290 | this.ignoreAnnotationsWhenAttributingComments = ignoreAnnotationsWhenAttributingComments; |
291 | return this; |
292 | } |
293 | |
294 | public ParserConfiguration setStoreTokens(boolean storeTokens) { |
295 | this.storeTokens = storeTokens; |
296 | if (!storeTokens) { |
297 | setAttributeComments(false); |
298 | } |
299 | return this; |
300 | } |
301 | |
302 | public boolean isStoreTokens() { |
303 | return storeTokens; |
304 | } |
305 | |
306 | public int getTabSize() { |
307 | return tabSize; |
308 | } |
309 | |
310 | /** |
311 | * When a TAB character is encountered during parsing, the column position will be increased by this value. |
312 | * By default it is 1. |
313 | */ |
314 | public ParserConfiguration setTabSize(int tabSize) { |
315 | this.tabSize = tabSize; |
316 | return this; |
317 | } |
318 | |
319 | /** |
320 | * Disabled by default. |
321 | * When this is enabled, LexicalPreservingPrinter.print can be used to reproduce |
322 | * the original formatting of the file. |
323 | */ |
324 | public ParserConfiguration setLexicalPreservationEnabled(boolean lexicalPreservationEnabled) { |
325 | this.lexicalPreservationEnabled = lexicalPreservationEnabled; |
326 | return this; |
327 | } |
328 | |
329 | public boolean isLexicalPreservationEnabled() { |
330 | return lexicalPreservationEnabled; |
331 | } |
332 | |
333 | /** |
334 | * Retrieve the SymbolResolver to be used while parsing, if any. |
335 | */ |
336 | public Optional<SymbolResolver> getSymbolResolver() { |
337 | return Optional.ofNullable(symbolResolver); |
338 | } |
339 | |
340 | /** |
341 | * Set the SymbolResolver to be injected while parsing. |
342 | */ |
343 | public ParserConfiguration setSymbolResolver(SymbolResolver symbolResolver) { |
344 | this.symbolResolver = symbolResolver; |
345 | return this; |
346 | } |
347 | |
348 | public List<Providers.PreProcessor> getPreProcessors() { |
349 | return preProcessors; |
350 | } |
351 | |
352 | public List<ParseResult.PostProcessor> getPostProcessors() { |
353 | return postProcessors; |
354 | } |
355 | |
356 | public ParserConfiguration setLanguageLevel(LanguageLevel languageLevel) { |
357 | this.languageLevel = languageLevel; |
358 | return this; |
359 | } |
360 | |
361 | public LanguageLevel getLanguageLevel() { |
362 | return languageLevel; |
363 | } |
364 | |
365 | /** |
366 | * When set to true, unicode escape handling is done by preprocessing the whole input, |
367 | * meaning that all unicode escapes are turned into unicode characters before parsing. |
368 | * That means the AST will never contain literal unicode escapes. However, |
369 | * positions in the AST will point to the original input, which is exactly the same as without this option. |
370 | * Without this option enabled, the unicode escapes will not be processed and are transfered intact to the AST. |
371 | */ |
372 | public ParserConfiguration setPreprocessUnicodeEscapes(boolean preprocessUnicodeEscapes) { |
373 | this.preprocessUnicodeEscapes = preprocessUnicodeEscapes; |
374 | return this; |
375 | } |
376 | |
377 | public boolean isPreprocessUnicodeEscapes() { |
378 | return preprocessUnicodeEscapes; |
379 | } |
380 | |
381 | public ParserConfiguration setDetectOriginalLineSeparator(boolean detectOriginalLineSeparator) { |
382 | this.detectOriginalLineSeparator = detectOriginalLineSeparator; |
383 | return this; |
384 | } |
385 | |
386 | public boolean isDetectOriginalLineSeparator() { |
387 | return detectOriginalLineSeparator; |
388 | } |
389 | |
390 | public Charset getCharacterEncoding() { |
391 | return characterEncoding; |
392 | } |
393 | |
394 | /** |
395 | * The character encoding used for reading input from files and streams. By default UTF8 is used. |
396 | */ |
397 | public ParserConfiguration setCharacterEncoding(Charset characterEncoding) { |
398 | this.characterEncoding = characterEncoding; |
399 | return this; |
400 | } |
401 | |
402 | } |
403 |
Members