mirror of https://github.com/apache/lucene.git
353 lines
12 KiB
Groovy
353 lines
12 KiB
Groovy
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).")
|
|
text = text.replace(
|
|
"public class QueryParserTokenManager ",
|
|
'@SuppressWarnings("unused") public class QueryParserTokenManager ')
|
|
text = text.replace(
|
|
"public class StandardSyntaxParserTokenManager ",
|
|
'@SuppressWarnings("unused") public class StandardSyntaxParserTokenManager ')
|
|
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<>")
|
|
text = text.replace(
|
|
"final private LookaheadSuccess jj_ls =",
|
|
"static final private LookaheadSuccess jj_ls =")
|
|
text = text.replace(
|
|
"public class QueryParser ",
|
|
'@SuppressWarnings({"unused","null"}) public class QueryParser ')
|
|
text = text.replace(
|
|
"final public Query TopLevelQuery(",
|
|
"@Override final public Query TopLevelQuery(")
|
|
text = text.replace(
|
|
"public void ReInit(CharStream ",
|
|
"@Override public void ReInit(CharStream ")
|
|
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<>")
|
|
text = text.replace(
|
|
"public class QueryParser ",
|
|
'@SuppressWarnings({"unused","null"}) public class QueryParser ')
|
|
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")
|
|
text = text.replace(
|
|
"public class StandardSyntaxParser ",
|
|
'@SuppressWarnings({"unused","null"}) public class StandardSyntaxParser ')
|
|
return text
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
task javacc() {
|
|
description "Regenerate query parsers (javacc syntax definitions)."
|
|
group "generation"
|
|
|
|
dependsOn wrapWithPersistentChecksums(javaccParserClassic, [ andThenTasks: "spotlessApply" ]),
|
|
wrapWithPersistentChecksums(javaccParserSurround, [ andThenTasks: "spotlessApply" ]),
|
|
wrapWithPersistentChecksums(javaccParserFlexible, [ andThenTasks: "spotlessApply" ])
|
|
}
|
|
|
|
regenerate.dependsOn javacc
|
|
}
|
|
|
|
// We always regenerate, no need to declare outputs.
|
|
class JavaCCTask extends DefaultTask {
|
|
@InputFile
|
|
File javaccFile
|
|
|
|
/**
|
|
* Apply closures to all generated files before they're copied back
|
|
* to mainline code.
|
|
*/
|
|
List<Closure<FileTree>> afterGenerate = new ArrayList<>()
|
|
|
|
@OutputFiles
|
|
List<File> getGeneratedSources() {
|
|
// Return the list of generated files.
|
|
def baseDir = javaccFile.parentFile
|
|
def baseName = javaccFile.name.replace(".jj", "")
|
|
|
|
return [
|
|
project.file("${baseDir}/${baseName}.java"),
|
|
project.file("${baseDir}/${baseName}Constants.java"),
|
|
project.file("${baseDir}/${baseName}TokenManager.java"),
|
|
project.file("${baseDir}/ParseException.java"),
|
|
project.file("${baseDir}/Token.java"),
|
|
project.file("${baseDir}/TokenMgrError.java")
|
|
]
|
|
}
|
|
|
|
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("Recompiling JavaCC: ${project.rootDir.relativePath(javaccFile)}")
|
|
|
|
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"
|
|
}
|
|
}
|
|
}
|