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