| import java.nio.charset.Charset |
| import java.util.function.Function |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // This adds javacc generation support. |
| |
| configure(rootProject) { |
| configurations { |
| javacc |
| } |
| |
| dependencies { |
| javacc "net.java.dev.javacc:javacc:${scriptDepVersions['javacc']}" |
| } |
| |
| task javacc() { |
| description "Regenerate sources for corresponding javacc grammar files." |
| group "generation" |
| |
| dependsOn allprojects.collect { prj -> prj.tasks.withType(JavaCCTask) } |
| } |
| } |
| |
| /** |
| * Utility function to read a file, apply changes to its content and write it back. |
| */ |
| def modifyFile = { File path, Function<String, String> modify -> |
| Function<String, String> normalizeEols = { text -> text.replace("\r\n", "\n") } |
| modify = normalizeEols.andThen(modify).andThen(normalizeEols) |
| |
| String original = path.getText("UTF-8") |
| String modified = modify.apply(original) |
| if (!original.equals(modified)) { |
| path.write(modified, "UTF-8") |
| } |
| } |
| |
| def commonCleanups = { FileTree generatedFiles -> |
| // This is a minor typo in a comment that nonetheless people have hand-corrected in the past. |
| generatedFiles.matching({ include "CharStream.java" }).each {file -> |
| modifyFile(file, { text -> |
| return text.replace( |
| "implemetation", |
| "implementation"); |
| }) |
| } |
| |
| generatedFiles.each {file -> |
| modifyFile(file, { text -> |
| // Normalize EOLs and tabs (EOLs are a side-effect of modifyFile). |
| text = text.replace("\t", " "); |
| text = text.replaceAll("JavaCC - OriginalChecksum=[^*]+", "(filtered)") |
| text = text.replace("StringBuffer", "StringBuilder") |
| return text |
| }) |
| } |
| |
| generatedFiles.matching({ include "*TokenManager.java" }).each { file -> |
| modifyFile(file, { text -> |
| // Remove redundant imports. |
| text = text.replaceAll( |
| /(?m)^import .+/, |
| "") |
| // Add CharStream imports. |
| text = text.replaceAll( |
| /package (.+)/, |
| ''' |
| package $1 |
| import org.apache.lucene.queryparser.charstream.CharStream; |
| '''.trim()) |
| // Eliminates redundant cast message. |
| text = text.replace( |
| "int hiByte = (int)(curChar >> 8);", |
| "int hiByte = curChar >> 8;") |
| // Access to forbidden APIs. |
| text = text.replace( |
| "public java.io.PrintStream debugStream = System.out;", |
| "// (debugStream omitted).") |
| text = text.replace( |
| "public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }", |
| "// (setDebugStream omitted).") |
| return text |
| }) |
| } |
| } |
| |
| configure(project(":lucene:queryparser")) { |
| task javaccParserClassic(type: JavaCCTask) { |
| description "Regenerate classic query parser from lucene/queryparser/classic/QueryParser.jj" |
| group "generation" |
| |
| javaccFile = file('src/java/org/apache/lucene/queryparser/classic/QueryParser.jj') |
| |
| afterGenerate << commonCleanups |
| afterGenerate << { FileTree generatedFiles -> |
| generatedFiles.matching { include "QueryParser.java" }.each { file -> |
| modifyFile(file, { text -> |
| text = text.replace( |
| "public QueryParser(CharStream ", |
| "protected QueryParser(CharStream ") |
| text = text.replace( |
| "public QueryParser(QueryParserTokenManager ", |
| "protected QueryParser(QueryParserTokenManager ") |
| text = text.replace( |
| "new java.util.ArrayList<int[]>", |
| "new java.util.ArrayList<>") |
| return text |
| }) |
| } |
| } |
| } |
| |
| task javaccParserSurround(type: JavaCCTask) { |
| description "Regenerate surround query parser from lucene/queryparser/surround/parser/QueryParser.jj" |
| group "generation" |
| |
| javaccFile = file('src/java/org/apache/lucene/queryparser/surround/parser/QueryParser.jj') |
| |
| afterGenerate << commonCleanups |
| afterGenerate << { FileTree generatedFiles -> |
| generatedFiles.matching { include "QueryParser.java" }.each { file -> |
| modifyFile(file, { text -> |
| text = text.replace( |
| "import org.apache.lucene.analysis.TokenStream;", |
| "") |
| text = text.replace( |
| "new java.util.ArrayList<int[]>", |
| "new java.util.ArrayList<>") |
| return text |
| }) |
| } |
| } |
| } |
| |
| task javaccParserFlexible(type: JavaCCTask) { |
| description "Regenerate Flexible query parser from queryparser/flexible/standard/parser/StandardSyntaxParser.jj" |
| group "generation" |
| |
| javaccFile = file('src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj') |
| |
| afterGenerate << commonCleanups |
| afterGenerate << { FileTree generatedFiles -> |
| generatedFiles.matching { include "ParseException.java" }.each { file -> |
| modifyFile(file, { text -> |
| // Modify constructor. |
| text = text.replace( |
| "class ParseException extends Exception", |
| "class ParseException extends QueryNodeParseException") |
| |
| // Modify imports. |
| text = text.replace( |
| "package org.apache.lucene.queryparser.flexible.standard.parser;", '''\ |
| package org.apache.lucene.queryparser.flexible.standard.parser; |
| |
| import org.apache.lucene.queryparser.flexible.messages.*; |
| import org.apache.lucene.queryparser.flexible.core.*; |
| import org.apache.lucene.queryparser.flexible.core.messages.*; |
| ''') |
| |
| // Modify constructors and code bits |
| text = text.replaceAll( |
| /(?s)[ ]*public ParseException\(Token currentTokenVal[^}]+[}]/, '''\ |
| public ParseException(Token currentTokenVal, |
| int[][] expectedTokenSequencesVal, String[] tokenImageVal) |
| { |
| super(new MessageImpl(QueryParserMessages.INVALID_SYNTAX, initialise( |
| currentTokenVal, expectedTokenSequencesVal, tokenImageVal))); |
| this.currentToken = currentTokenVal; |
| this.expectedTokenSequences = expectedTokenSequencesVal; |
| this.tokenImage = tokenImageVal; |
| } |
| ''') |
| |
| text = text.replaceAll( |
| /(?s)[ ]*public ParseException\(String message\)[^}]+[}]/, '''\ |
| public ParseException(Message message) |
| { |
| super(message); |
| } |
| ''') |
| |
| text = text.replaceAll( |
| /(?s)[ ]*public ParseException\(\)[^}]+[}]/, '''\ |
| public ParseException() |
| { |
| super(new MessageImpl(QueryParserMessages.INVALID_SYNTAX, "Error")); |
| } |
| ''') |
| return text |
| }) |
| } |
| |
| generatedFiles.matching { include "StandardSyntaxParser.java" }.each { file -> |
| modifyFile(file, { text -> |
| // Remove redundant cast |
| text = text.replace( |
| "new java.util.ArrayList<int[]>", |
| "new java.util.ArrayList<>") |
| text = text.replace( |
| "new ArrayList<QueryNode>()", |
| "new ArrayList<>()") |
| text = text.replace( |
| "Collections.<QueryNode> singletonList", |
| "Collections.singletonList") |
| return text |
| }) |
| } |
| } |
| } |
| |
| task javacc() { |
| description "Regenerate query parsers (javacc syntax definitions)." |
| group "generation" |
| |
| dependsOn javaccParserClassic |
| dependsOn javaccParserSurround |
| dependsOn javaccParserFlexible |
| } |
| } |
| |
| configure(project(":solr:core")) { |
| task javacc(type: JavaCCTask) { |
| description "Regenerate Solr query parser" |
| group "generation" |
| |
| javaccFile = file('src/java/org/apache/solr/parser/QueryParser.jj') |
| |
| |
| afterGenerate << commonCleanups |
| afterGenerate << { FileTree generatedFiles -> |
| generatedFiles.matching { include "QueryParser.java" }.each { file -> |
| modifyFile(file, { text -> |
| text = text.replace( |
| "public QueryParser(CharStream ", |
| "protected QueryParser(CharStream ") |
| text = text.replace( |
| "public QueryParser(QueryParserTokenManager ", |
| "protected QueryParser(QueryParserTokenManager ") |
| text = text.replace( |
| "final private LookaheadSuccess jj_ls =", |
| "static final private LookaheadSuccess jj_ls =") |
| return text |
| }) |
| } |
| } |
| } |
| } |
| |
| |
| |
| // We always regenerate, no need to declare outputs. |
| class JavaCCTask extends DefaultTask { |
| @Input |
| File javaccFile |
| |
| /** |
| * Apply closures to all generated files before they're copied back |
| * to mainline code. |
| */ |
| @Optional |
| @Input |
| List<Closure<FileTree>> afterGenerate = new ArrayList<>() |
| |
| JavaCCTask() { |
| dependsOn(project.rootProject.configurations.javacc) |
| } |
| |
| @TaskAction |
| def generate() { |
| if (!javaccFile || !javaccFile.exists()) { |
| throw new GradleException("Input file does not exist: ${javaccFile}") |
| } |
| |
| // Run javacc generation into temporary folder so that we know all the generated files |
| // and can post-process them easily. |
| def tempDir = this.getTemporaryDir() |
| tempDir.mkdirs() |
| project.delete project.fileTree(tempDir, { include: "**/*.java" }) |
| |
| def targetDir = javaccFile.parentFile |
| logger.lifecycle("Regenerating JavaCC:\n from: ${javaccFile}\n to: ${targetDir}") |
| |
| def output = new ByteArrayOutputStream() |
| def result = project.javaexec { |
| classpath { |
| project.rootProject.configurations.javacc |
| } |
| |
| ignoreExitValue = true |
| standardOutput = output |
| errorOutput = output |
| |
| main = "org.javacc.parser.Main" |
| args += [ |
| "-OUTPUT_DIRECTORY=${tempDir}", |
| javaccFile |
| ] |
| } |
| |
| // Unless we request verbose logging, don't emit javacc output. |
| if (result.exitValue != 0) { |
| throw new GradleException("JavaCC failed to compile ${javaccFile}, here is the compilation output:\n${output}") |
| } |
| |
| // Make sure we don't have warnings. |
| if (output.toString(Charset.defaultCharset()).contains("Warning:")) { |
| throw new GradleException("JavaCC emitted warnings for ${javaccFile}, here is the compilation output:\n${output}") |
| } |
| |
| // Apply any custom modifications. |
| def generatedFiles = project.fileTree(tempDir) |
| |
| afterGenerate.each {closure -> |
| closure.call(generatedFiles) |
| } |
| |
| // Copy back to mainline sources. |
| project.copy { |
| from tempDir |
| into targetDir |
| |
| // We don't need CharStream interface as we redirect to our own. |
| exclude "CharStream.java" |
| } |
| } |
| } |