LUCENE-5207: initial patch from ryan

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene5207@1522767 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Robert Muir 2013-09-13 03:11:17 +00:00
parent bd94c46203
commit c73f175c71
37 changed files with 7361 additions and 1 deletions

View File

@ -651,7 +651,7 @@ def verifyUnpacked(project, artifact, unpackPath, svnRevision, version, testArgs
if project == 'lucene':
# TODO: clean this up to not be a list of modules that we must maintain
extras = ('analysis', 'benchmark', 'classification', 'codecs', 'core', 'demo', 'docs', 'facet', 'grouping', 'highlighter', 'join', 'memory', 'misc', 'queries', 'queryparser', 'replicator', 'sandbox', 'spatial', 'suggest', 'test-framework', 'licenses')
extras = ('analysis', 'benchmark', 'classification', 'codecs', 'core', 'demo', 'docs', 'expressions', 'facet', 'grouping', 'highlighter', 'join', 'memory', 'misc', 'queries', 'queryparser', 'replicator', 'sandbox', 'spatial', 'suggest', 'test-framework', 'licenses')
if isSrc:
extras += ('build.xml', 'common-build.xml', 'module-build.xml', 'ivy-settings.xml', 'backwards', 'tools', 'site')
else:

View File

@ -283,6 +283,7 @@
<!-- codecs: problems -->
<!-- core: problems -->
<check-missing-javadocs dir="build/docs/demo" level="method"/>
<check-missing-javadocs dir="build/docs/expressions" level="method"/>
<!-- facet: problems -->
<!-- grouping: problems -->
<!-- highlighter: problems -->

View File

@ -1567,6 +1567,8 @@ ${tests-output}/junit4-*.suites - per-JVM executed suites
<pattern substring="This file was generated automatically by the Snowball to Java compiler"/>
<!-- uima tests generated by JCasGen -->
<pattern substring="First created by JCasGen"/>
<!-- parsers generated by antlr -->
<pattern substring="ANTLR GENERATED CODE"/>
</rat:substringMatcher>
<!-- built in approved licenses -->

View File

@ -44,6 +44,13 @@ public final class MathUtil {
return ret;
}
/**
* Calculates logarithm in a given base with doubles.
*/
public static double log(double base, double x) {
return Math.log(x) / Math.log(base);
}
/** Return the greatest common divisor of <code>a</code> and <code>b</code>,
* consistently with {@link BigInteger#gcd(BigInteger)}.
* <p><b>NOTE</b>: A greatest common divisor must be positive, but
@ -78,4 +85,69 @@ public final class MathUtil {
}
return a << commonTrailingZeros;
}
/**
* Calculates inverse hyperbolic sine of a {@code double} value.
* <p>
* Special cases:
* <ul>
* <li>If the argument is NaN, then the result is NaN.
* <li>If the argument is zero, then the result is a zero with the same sign as the argument.
* <li>If the argument is infinite, then the result is infinity with the same sign as the argument.
* </ul>
*/
public static double asinh(double a) {
final double sign;
// check the sign bit of the raw representation to handle -0
if (Double.doubleToRawLongBits(a) < 0) {
a = Math.abs(a);
sign = -1.0d;
} else {
sign = 1.0d;
}
return sign * Math.log(Math.sqrt(a * a + 1.0d) + a);
}
/**
* Calculates inverse hyperbolic cosine of a {@code double} value.
* <p>
* Special cases:
* <ul>
* <li>If the argument is NaN, then the result is NaN.
* <li>If the argument is +1, then the result is a zero.
* <li>If the argument is positive infinity, then the result is positive infinity.
* <li>If the argument is less than 1, then the result is NaN.
* </ul>
*/
public static double acosh(double a) {
return Math.log(Math.sqrt(a * a - 1.0d) + a);
}
/**
* Calculates inverse hyperbolic tangent of a {@code double} value.
* <p>
* Special cases:
* <ul>
* <li>If the argument is NaN, then the result is NaN.
* <li>If the argument is zero, then the result is a zero with the same sign as the argument.
* <li>If the argument is +1, then the result is positive infinity.
* <li>If the argument is -1, then the result is negative infinity.
* <li>If the argument's absolute value is greater than 1, then the result is NaN.
* </ul>
*/
public static double atanh(double a) {
final double mult;
// check the sign bit of the raw representation to handle -0
if (Double.doubleToRawLongBits(a) < 0) {
a = Math.abs(a);
mult = -0.5d;
} else {
mult = 0.5d;
}
return mult * Math.log((1.0d + a) / (1.0d - a));
}
}

View File

@ -102,4 +102,80 @@ public class TestMathUtil extends LuceneTestCase {
assertEquals(Long.MIN_VALUE, MathUtil.gcd(Long.MIN_VALUE, Long.MIN_VALUE));
}
public void testAcoshMethod() {
// acosh(NaN) == NaN
assertTrue(Double.isNaN(MathUtil.acosh(Double.NaN)));
// acosh(1) == +0
assertEquals(0, Double.doubleToLongBits(MathUtil.acosh(1D)));
// acosh(POSITIVE_INFINITY) == POSITIVE_INFINITY
assertEquals(Double.doubleToLongBits(Double.POSITIVE_INFINITY),
Double.doubleToLongBits(MathUtil.acosh(Double.POSITIVE_INFINITY)));
// acosh(x) : x < 1 == NaN
assertTrue(Double.isNaN(MathUtil.acosh(0.9D))); // x < 1
assertTrue(Double.isNaN(MathUtil.acosh(0D))); // x == 0
assertTrue(Double.isNaN(MathUtil.acosh(-0D))); // x == -0
assertTrue(Double.isNaN(MathUtil.acosh(-0.9D))); // x < 0
assertTrue(Double.isNaN(MathUtil.acosh(-1D))); // x == -1
assertTrue(Double.isNaN(MathUtil.acosh(-10D))); // x < -1
assertTrue(Double.isNaN(MathUtil.acosh(Double.NEGATIVE_INFINITY))); // x == -Inf
double epsilon = 0.000001;
assertEquals(0, MathUtil.acosh(1), epsilon);
assertEquals(1.5667992369724109, MathUtil.acosh(2.5), epsilon);
assertEquals(14.719378760739708, MathUtil.acosh(1234567.89), epsilon);
}
public void testAsinhMethod() {
// asinh(NaN) == NaN
assertTrue(Double.isNaN(MathUtil.asinh(Double.NaN)));
// asinh(+0) == +0
assertEquals(0, Double.doubleToLongBits(MathUtil.asinh(0D)));
// asinh(-0) == -0
assertEquals(Double.doubleToLongBits(-0D), Double.doubleToLongBits(MathUtil.asinh(-0D)));
// asinh(POSITIVE_INFINITY) == POSITIVE_INFINITY
assertEquals(Double.doubleToLongBits(Double.POSITIVE_INFINITY),
Double.doubleToLongBits(MathUtil.asinh(Double.POSITIVE_INFINITY)));
// asinh(NEGATIVE_INFINITY) == NEGATIVE_INFINITY
assertEquals(Double.doubleToLongBits(Double.NEGATIVE_INFINITY),
Double.doubleToLongBits(MathUtil.asinh(Double.NEGATIVE_INFINITY)));
double epsilon = 0.000001;
assertEquals(-14.719378760740035, MathUtil.asinh(-1234567.89), epsilon);
assertEquals(-1.6472311463710958, MathUtil.asinh(-2.5), epsilon);
assertEquals(-0.8813735870195429, MathUtil.asinh(-1), epsilon);
assertEquals(0, MathUtil.asinh(0), 0);
assertEquals(0.8813735870195429, MathUtil.asinh(1), epsilon);
assertEquals(1.6472311463710958, MathUtil.asinh(2.5), epsilon);
assertEquals(14.719378760740035, MathUtil.asinh(1234567.89), epsilon );
}
public void testAtanhMethod() {
// atanh(NaN) == NaN
assertTrue(Double.isNaN(MathUtil.atanh(Double.NaN)));
// atanh(+0) == +0
assertEquals(0, Double.doubleToLongBits(MathUtil.atanh(0D)));
// atanh(-0) == -0
assertEquals(Double.doubleToLongBits(-0D),
Double.doubleToLongBits(MathUtil.atanh(-0D)));
// atanh(1) == POSITIVE_INFINITY
assertEquals(Double.doubleToLongBits(Double.POSITIVE_INFINITY),
Double.doubleToLongBits(MathUtil.atanh(1D)));
// atanh(-1) == NEGATIVE_INFINITY
assertEquals(Double.doubleToLongBits(Double.NEGATIVE_INFINITY),
Double.doubleToLongBits(MathUtil.atanh(-1D)));
// atanh(x) : Math.abs(x) > 1 == NaN
assertTrue(Double.isNaN(MathUtil.atanh(1.1D))); // x > 1
assertTrue(Double.isNaN(MathUtil.atanh(Double.POSITIVE_INFINITY))); // x == Inf
assertTrue(Double.isNaN(MathUtil.atanh(-1.1D))); // x < -1
assertTrue(Double.isNaN(MathUtil.atanh(Double.NEGATIVE_INFINITY))); // x == -Inf
double epsilon = 0.000001;
assertEquals(Double.NEGATIVE_INFINITY, MathUtil.atanh(-1), 0);
assertEquals(-0.5493061443340549, MathUtil.atanh(-0.5), epsilon);
assertEquals(0, MathUtil.atanh(0), 0);
assertEquals(0.5493061443340549, MathUtil.atanh(0.5), epsilon);
assertEquals(Double.POSITIVE_INFINITY, MathUtil.atanh(1), 0);
}
}

View File

@ -0,0 +1,103 @@
<?xml version="1.0"?>
<!--
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.
-->
<project name="expressions" default="default">
<description>
Dynamically computed values to sort/facet/search on based on a pluggable grammar.
</description>
<!-- some files for testing that do not have license headers -->
<property name="rat.excludes" value="**/*.tokens"/>
<import file="../module-build.xml"/>
<path id="classpath">
<path refid="base.classpath"/>
<fileset dir="lib"/>
<pathelement path="${queries.jar}"/>
</path>
<path id="test.classpath">
<path refid="test.base.classpath"/>
<fileset dir="lib"/>
<pathelement path="src/test-files"/>
</path>
<target name="compile-core" depends="jar-queries,common.compile-core" />
<target name="javadocs" depends="javadocs-queries,compile-core">
<invoke-module-javadoc>
<links>
<link href="../queries"/>
</links>
</invoke-module-javadoc>
</target>
<target name="regenerate" depends="run-antlr"/>
<target name="resolve-antlr" xmlns:ivy="antlib:org.apache.ivy.ant">
<ivy:cachepath organisation="org.antlr" module="antlr" revision="3.4"
inline="true" conf="default" type="jar" pathid="antlr.classpath"/>
</target>
<target name="run-antlr" depends="resolve-antlr">
<regen-grammar package="js" grammar="Javascript"/>
</target>
<macrodef name="regen-grammar">
<attribute name="package" />
<attribute name="grammar" />
<sequential>
<!-- delete parser and lexer so files will be generated -->
<delete>
<fileset dir="src/java/org/apache/lucene/expressions/@{package}">
<include name="@{grammar}Lexer.java" />
<include name="@{grammar}Parser.java" />
</fileset>
</delete>
<local name="grammar.path"/>
<local name="grammar.relative.path"/>
<property name="grammar.path" location="src/java/org/apache/lucene/expressions/@{package}"/>
<property name="grammar.relative.path" location="${grammar.path}" relative="true"/>
<java classname="org.antlr.Tool" fork="true" failonerror="true" classpathref="antlr.classpath">
<arg value="-verbose"/>
<arg value="-make"/>
<arg value="-o"/>
<arg path="${grammar.path}"/>
<arg path="${grammar.path}/@{grammar}.g"/>
</java>
<!-- prevent warnings from generated code -->
<replaceregexp file="${grammar.path}/@{grammar}Parser.java"
encoding="UTF-8"
byline="true">
<regexp pattern="@SuppressWarnings(.*)" />
<substitution expression="@SuppressWarnings({&quot;all&quot;, &quot;warnings&quot;, &quot;unchecked&quot;, &quot;cast&quot;})" />
</replaceregexp>
<!-- replace absolute paths by relative ones -->
<replace file="${grammar.path}/@{grammar}Parser.java" token="${grammar.path}" value="${grammar.relative.path}" encoding="UTF-8"/>
<replace file="${grammar.path}/@{grammar}Lexer.java" token="${grammar.path}" value="${grammar.relative.path}" encoding="UTF-8"/>
<!-- make the generated classes package private (it's an antlr option with 4.0) -->
<replace file="${grammar.path}/@{grammar}Parser.java" token="public class @{grammar}Parser" value="class @{grammar}Parser" encoding="UTF-8"/>
<replace file="${grammar.path}/@{grammar}Lexer.java" token="public class @{grammar}Lexer" value="class @{grammar}Lexer" encoding="UTF-8"/>
<!-- nuke timestamps in generated files -->
<replaceregexp file="${grammar.path}/@{grammar}Parser.java" match=".*" replace="\/\/ ANTLR GENERATED CODE: DO NOT EDIT" encoding="UTF-8"/>
<replaceregexp file="${grammar.path}/@{grammar}Lexer.java" match=".*" replace="\/\/ ANTLR GENERATED CODE: DO NOT EDIT" encoding="UTF-8"/>
</sequential>
</macrodef>
</project>

View File

@ -0,0 +1,26 @@
<!--
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.
-->
<ivy-module version="2.0">
<info organisation="org.apache.lucene" module="expressions"/>
<dependencies>
<dependency org="org.antlr" name="antlr-runtime" rev="3.4" transitive="false"/>
<dependency org="org.ow2.asm" name="asm" rev="4.1" transitive="false"/>
<exclude org="*" ext="*" matcher="regexp" type="${ivy.exclude.types}"/>
</dependencies>
</ivy-module>

View File

@ -0,0 +1,87 @@
package org.apache.lucene.expressions;
/*
* 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.
*/
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.lucene.queries.function.ValueSource;
/**
* Binds variable names in expressions to actual data.
* <p>
* These are typically DocValues fields/FieldCache, the document's
* relevance score, or other ValueSources.
*
* @lucene.experimental
*/
public abstract class Bindings implements Iterable<String> {
/** Sole constructor. (For invocation by subclass
* constructors, typically implicit.) */
protected Bindings() {}
/**
* Returns a ValueSource bound to the variable name.
*/
public abstract ValueSource getValueSource(String name);
/** Returns an <code>Iterator</code> over the variable names in this binding */
@Override
public abstract Iterator<String> iterator();
/**
* Traverses the graph of bindings, checking there are no cycles or missing references
* @throws IllegalArgumentException if the bindings is inconsistent
*/
public final void validate() {
Set<String> marked = new HashSet<String>();
Set<String> chain = new HashSet<String>();
for (String name : this) {
if (!marked.contains(name)) {
chain.add(name);
validate(name, marked, chain);
chain.remove(name);
}
}
}
private void validate(String name, Set<String> marked, Set<String> chain) {
ValueSource vs = getValueSource(name);
if (vs == null) {
throw new IllegalArgumentException("Invalid reference '" + name + "'");
}
if (vs instanceof ExpressionValueSource) {
Expression expr = ((ExpressionValueSource)vs).expression;
for (String external : expr.externals) {
if (chain.contains(external)) {
throw new IllegalArgumentException("Recursion Error: Cycle detected originating in (" + external + ")");
}
if (!marked.contains(external)) {
chain.add(external);
validate(external, marked, chain);
chain.remove(external);
}
}
}
marked.add(name);
}
}

View File

@ -0,0 +1,84 @@
package org.apache.lucene.expressions;
/*
* 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.
*/
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.SortField;
/**
* Base class that computes the value of an expression for a document.
* <p>
* Example usage:
* <pre class="prettyprint">
* // compile an expression:
* Expression expr = JavascriptCompiler.compile("sqrt(_score) + ln(popularity)");
*
* // SimpleBindings just maps variables to SortField instances
* SimpleBindings bindings = new SimpleBindings();
* bindings.add(new SortField("_score", SortField.Type.SCORE));
* bindings.add(new SortField("popularity", SortField.Type.INT));
*
* // create a sort field and sort by it (reverse order)
* Sort sort = new Sort(expr.getSortField(bindings, true));
* Query query = new TermQuery(new Term("body", "contents"));
* searcher.search(query, null, 10, sort);
* </pre>
* @lucene.experimental
*/
public abstract class Expression {
/** The original {@link String} expression, before it was compiled */
public final String expression;
/** The names of external references found in the expression */
public final String[] externals;
/**
* Creates a new {@code CompiledExpression}.
*
* @param expression The expression that was compiled
* @param externals Names of external references found in the expression
*/
public Expression(String expression, String[] externals) {
this.expression = expression;
this.externals = externals;
}
/**
* Evaluates the expression for the given document.
*
* @param document <code>docId</code> of the document to compute a value for
* @param functionValues {@link FunctionValues} for each element of {@link #externals}.
* @return The computed value of the expression for the given document.
*/
public abstract double evaluate(int document, FunctionValues[] functionValues);
/**
* Get a value source which can compute the value of this expression in the context of the given bindings.
* @param bindings Bindings to use for external values in this expression
* @return A value source which will evaluate this expression when used
*/
public ValueSource getValueSource(Bindings bindings) {
return new ExpressionValueSource(bindings, this);
}
/** Get a sort field which can be used to rank documents by this expression. */
public SortField getSortField(Bindings bindings, boolean reverse) {
return getValueSource(bindings).getSortField(reverse);
}
}

View File

@ -0,0 +1,95 @@
package org.apache.lucene.expressions;
/*
* 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.
*/
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.FieldComparator;
import org.apache.lucene.search.Scorer;
/** A custom comparator for sorting documents by an expression */
class ExpressionComparator extends FieldComparator<Double> {
private final double[] values;
private double bottom;
private ValueSource source;
private FunctionValues scores;
private AtomicReaderContext readerContext;
public ExpressionComparator(ValueSource source, int numHits) {
values = new double[numHits];
this.source = source;
}
// TODO: change FieldComparator.setScorer to throw IOException and remove this try-catch
@Override
public void setScorer(Scorer scorer) {
super.setScorer(scorer);
// TODO: might be cleaner to lazy-init 'source' and set scorer after?
assert readerContext != null;
try {
Map<String,Object> context = new HashMap<String,Object>();
assert scorer != null;
context.put("scorer", new ScoreFunctionValues(scorer));
context.put("valuesCache", new HashMap<String, FunctionValues>());
scores = source.getValues(context, readerContext);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public int compare(int slot1, int slot2) {
return Double.compare(values[slot1], values[slot2]);
}
@Override
public void setBottom(int slot) {
bottom = values[slot];
}
@Override
public int compareBottom(int doc) throws IOException {
return Double.compare(bottom, scores.doubleVal(doc));
}
@Override
public void copy(int slot, int doc) throws IOException {
values[slot] = scores.doubleVal(doc);
}
@Override
public FieldComparator<Double> setNextReader(AtomicReaderContext context) throws IOException {
this.readerContext = context;
return this;
}
@Override
public Double value(int slot) {
return Double.valueOf(values[slot]);
}
@Override
public int compareDocToValue(int doc, Double valueObj) throws IOException {
return Double.compare(scores.doubleVal(doc), valueObj.doubleValue());
}
}

View File

@ -0,0 +1,64 @@
package org.apache.lucene.expressions;
/*
* 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.
*/
import org.apache.lucene.queries.function.FunctionValues;
/** A {@link FunctionValues} which evaluates an expression */
class ExpressionFunctionValues extends FunctionValues {
final Expression expression;
final FunctionValues[] functionValues;
int currentDocument = -1;
double currentValue;
public ExpressionFunctionValues(Expression expression, FunctionValues[] functionValues) {
if (expression == null) {
throw new NullPointerException();
}
if (functionValues == null) {
throw new NullPointerException();
}
this.expression = expression;
this.functionValues = functionValues;
}
@Override
public double doubleVal(int document) {
if (currentDocument != document) {
currentDocument = document;
currentValue = expression.evaluate(document, functionValues);
}
return currentValue;
}
@Override
public Object objectVal(int doc) {
return doubleVal(doc);
}
@Override
public String toString(int document) {
if (currentDocument != document) {
currentDocument = document;
currentValue = expression.evaluate(document, functionValues);
}
return "ExpressionFunctionValues(" + document + ": " + currentValue + ")";
}
}

View File

@ -0,0 +1,38 @@
package org.apache.lucene.expressions;
/*
* 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.
*/
import java.io.IOException;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.FieldComparator;
import org.apache.lucene.search.SortField;
/** A {@link SortField} which sorts documents by the evaluated value of an expression for each document */
class ExpressionSortField extends SortField {
private final ValueSource source;
ExpressionSortField(String name, ValueSource source, boolean reverse) {
super(name, Type.CUSTOM, reverse);
this.source = source;
}
@Override
public FieldComparator<?> getComparator(final int numHits, final int sortPos) throws IOException {
return new ExpressionComparator(source, numHits);
}
}

View File

@ -0,0 +1,88 @@
package org.apache.lucene.expressions;
/*
* 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.
*/
import java.io.IOException;
import java.util.Map;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.SortField;
/**
* A {@link ValueSource} which evaluates a {@link Expression} given the context of an {@link Bindings}.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
final class ExpressionValueSource extends ValueSource {
private final Bindings bindings;
final Expression expression;
public ExpressionValueSource(Bindings bindings, Expression expression) {
if (bindings == null) throw new NullPointerException();
if (expression == null) throw new NullPointerException();
this.bindings = bindings;
this.expression = expression;
}
/** <code>context</code> must contain a key <code>"valuesCache"</code> which is a <code>Map&lt;String,FunctionValues&gt;</code>. */
@Override
public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
ValueSource source;
Map<String, FunctionValues> valuesCache = (Map<String, FunctionValues>)context.get("valuesCache");
if (valuesCache == null) {
throw new NullPointerException();
}
FunctionValues[] externalValues = new FunctionValues[expression.externals.length];
for (int i = 0; i < expression.externals.length; ++i) {
String externalName = expression.externals[i];
FunctionValues values = valuesCache.get(externalName);
if (values == null) {
source = bindings.getValueSource(externalName);
values = source.getValues(context, readerContext);
if (values == null) {
throw new RuntimeException("Internal error. External (" + externalName + ") does not exist.");
}
valuesCache.put(externalName, values);
}
externalValues[i] = values;
}
return new ExpressionFunctionValues(expression, externalValues);
}
@Override
public SortField getSortField(boolean reverse) {
return new ExpressionSortField(expression.expression, this, reverse);
}
@Override
public String description() {
return "ExpressionValueSource(" + expression.expression + ")";
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return obj == this;
}
}

View File

@ -0,0 +1,48 @@
package org.apache.lucene.expressions;
/*
* 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.
*/
import java.io.IOException;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.search.Scorer;
/**
* A utility class to allow expressions to access the score as a {@link FunctionValues}.
*/
class ScoreFunctionValues extends FunctionValues {
final Scorer scorer;
ScoreFunctionValues(Scorer scorer) {
this.scorer = scorer;
}
@Override
public double doubleVal(int document) {
try {
assert document == scorer.docID();
return scorer.score();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
@Override
public String toString(int document) {
return "ComputedScorerValues(" + document + ": " + doubleVal(document) + ")";
}
}

View File

@ -0,0 +1,60 @@
package org.apache.lucene.expressions;
/*
* 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.
*/
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import java.io.IOException;
import java.util.Map;
/**
* A {@link ValueSource} which uses the {@link ScoreFunctionValues} passed through
* the context map by {@link ExpressionComparator}.
*/
@SuppressWarnings({"rawtypes"})
class ScoreValueSource extends ValueSource {
/**
* <code>context</code> must contain a key "scorer" which is a {@link FunctionValues}.
*/
@Override
public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
FunctionValues v = (FunctionValues) context.get("scorer");
if (v == null) {
throw new NullPointerException();
}
return v;
}
@Override
public boolean equals(Object o) {
return o == this;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public String description() {
return "ValueSource to expose scorer passed by ExpressionComparator";
}
}

View File

@ -0,0 +1,94 @@
package org.apache.lucene.expressions;
/*
* 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.
*/
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.queries.function.valuesource.DoubleFieldSource;
import org.apache.lucene.queries.function.valuesource.FloatFieldSource;
import org.apache.lucene.queries.function.valuesource.IntFieldSource;
import org.apache.lucene.queries.function.valuesource.LongFieldSource;
import org.apache.lucene.search.FieldCache.DoubleParser;
import org.apache.lucene.search.FieldCache.FloatParser;
import org.apache.lucene.search.FieldCache.IntParser;
import org.apache.lucene.search.FieldCache.LongParser;
import org.apache.lucene.search.SortField;
/**
* Simple class that binds expression variable names to {@link SortField}s.
*
* @lucene.experimental
*/
public final class SimpleBindings extends Bindings {
final Map<String,Object> map = new HashMap<String,Object>();
/** Creates a new empty Bindings */
public SimpleBindings() {}
/**
* Adds a SortField to the bindings.
* <p>
* This can be used to reference a DocValuesField, a field from
* FieldCache, the document's score, etc.
*/
public void add(SortField sortField) {
map.put(sortField.getField(), sortField);
}
/**
* Adds an Expression to the bindings.
* <p>
* This can be used to reference expressions from other expressions.
*/
public void add(String name, Expression expression) {
map.put(name, expression);
}
@Override
public ValueSource getValueSource(String name) {
Object o = map.get(name);
if (o == null) {
throw new IllegalArgumentException("Invalid reference '" + name + "'");
} else if (o instanceof Expression) {
return ((Expression)o).getValueSource(this);
}
SortField field = (SortField) o;
switch(field.getType()) {
case INT:
return new IntFieldSource(field.getField(), (IntParser) field.getParser());
case LONG:
return new LongFieldSource(field.getField(), (LongParser) field.getParser());
case FLOAT:
return new FloatFieldSource(field.getField(), (FloatParser) field.getParser());
case DOUBLE:
return new DoubleFieldSource(field.getField(), (DoubleParser) field.getParser());
case SCORE:
return new ScoreValueSource();
default:
throw new UnsupportedOperationException();
}
}
@Override
public Iterator<String> iterator() {
return map.keySet().iterator();
}
}

View File

@ -0,0 +1,381 @@
/*
Javascript.g
An expression syntax based on ECMAScript/Javascript.
This file was adapted from a general ECMAScript language definition at http://research.xebic.com/es3.
The major changes are the following:
* Stripped grammar of all parts not relevant for expression syntax.
* Stripped grammar of unicode character support.
* Added override function for customized error handling.
* Renaming of many grammar rules.
* Removal of annotations no longer relevant for stripped pieces.
The Original Copyright Notice is the following:
Copyrights 2008-2009 Xebic Reasearch BV. All rights reserved..
Original work by Patrick Hulsmeijer.
This ANTLR 3 LL(*) grammar is based on Ecma-262 3rd edition (JavaScript 1.5, JScript 5.5).
The annotations refer to the "A Grammar Summary" section (e.g. A.1 Lexical Grammar)
and the numbers in parenthesis to the paragraph numbers (e.g. (7.8) ).
This document is best viewed with ANTLRWorks (www.antlr.org).
Software License Agreement (BSD License)
Copyright (c) 2008-2010, Xebic Research B.V.
All rights reserved.
Redistribution and use of this software in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of Xebic Research B.V. nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of Xebic Research B.V.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// ***********************************************************************
// * ANTLRv3 grammar for A9 rank expression language.
// ***********************************************************************
grammar Javascript;
options {
language = Java;
output = AST;
ASTLabelType=CommonTree;
}
tokens
{
AT_LPAREN = '(' ;
AT_RPAREN = ')' ;
AT_DOT = '.' ;
AT_COMMA = ',' ;
AT_COLON = ':' ;
AT_COMP_LT = '<' ;
AT_COMP_LTE = '<=' ;
AT_COMP_EQ = '==' ;
AT_COMP_NEQ = '!=' ;
AT_COMP_GTE = '>=' ;
AT_COMP_GT = '>' ;
AT_BOOL_NOT = '!' ;
AT_BOOL_AND = '&&' ;
AT_BOOL_OR = '||' ;
AT_COND_QUE = '?' ;
AT_NEGATE ;
AT_ADD = '+' ;
AT_SUBTRACT = '-' ;
AT_MULTIPLY = '*' ;
AT_DIVIDE = '/' ;
AT_MODULO = '%' ;
AT_BIT_SHL = '<<' ;
AT_BIT_SHR = '>>' ;
AT_BIT_SHU = '>>>';
AT_BIT_AND = '&' ;
AT_BIT_OR = '|' ;
AT_BIT_XOR = '^' ;
AT_BIT_NOT = '~' ;
AT_CALL ;
}
// ***********************************************************************
// * Java Package
// ***********************************************************************
@lexer::header {
package org.apache.lucene.expressions.js;
import java.text.ParseException;
}
@parser::header {
package org.apache.lucene.expressions.js;
import java.text.ParseException;
}
// ***********************************************************************
// * Error Handling
// ***********************************************************************
@lexer::members {
@Override
public void displayRecognitionError(String[] tokenNames, RecognitionException re) {
String message = " unexpected character '" + (char)re.c
+ "' at position (" + re.charPositionInLine + ").";
ParseException parseException = new ParseException(message, re.charPositionInLine);
parseException.initCause(re);
throw new RuntimeException(parseException);
}
}
@parser::members {
@Override
public void displayRecognitionError(String[] tokenNames, RecognitionException re) {
String message;
if (re.token == null) {
message = " unknown error (missing token).";
}
else if (re instanceof UnwantedTokenException) {
message = " extraneous " + getReadableTokenString(re.token)
+ " at position (" + re.charPositionInLine + ").";
}
else if (re instanceof MissingTokenException) {
message = " missing " + getReadableTokenString(re.token)
+ " at position (" + re.charPositionInLine + ").";
}
else if (re instanceof NoViableAltException) {
switch (re.token.getType()) {
case EOF:
message = " unexpected end of expression.";
break;
default:
message = " invalid sequence of tokens near " + getReadableTokenString(re.token)
+ " at position (" + re.charPositionInLine + ").";
break;
}
}
else {
message = " unexpected token " + getReadableTokenString(re.token)
+ " at position (" + re.charPositionInLine + ").";
}
ParseException parseException = new ParseException(message, re.charPositionInLine);
parseException.initCause(re);
throw new RuntimeException(parseException);
}
public static String getReadableTokenString(Token token) {
if (token == null) {
return "unknown token";
}
switch (token.getType()) {
case AT_LPAREN:
return "open parenthesis '('";
case AT_RPAREN:
return "close parenthesis ')'";
case AT_COMP_LT:
return "less than '<'";
case AT_COMP_LTE:
return "less than or equal '<='";
case AT_COMP_GT:
return "greater than '>'";
case AT_COMP_GTE:
return "greater than or equal '>='";
case AT_COMP_EQ:
return "equal '=='";
case AT_NEGATE:
return "negate '!='";
case AT_BOOL_NOT:
return "boolean not '!'";
case AT_BOOL_AND:
return "boolean and '&&'";
case AT_BOOL_OR:
return "boolean or '||'";
case AT_COND_QUE:
return "conditional '?'";
case AT_ADD:
return "addition '+'";
case AT_SUBTRACT:
return "subtraction '-'";
case AT_MULTIPLY:
return "multiplication '*'";
case AT_DIVIDE:
return "division '/'";
case AT_MODULO:
return "modulo '\%'";
case AT_BIT_SHL:
return "bit shift left '<<'";
case AT_BIT_SHR:
return "bit shift right '>>'";
case AT_BIT_SHU:
return "unsigned bit shift right '>>>'";
case AT_BIT_AND:
return "bitwise and '&'";
case AT_BIT_OR:
return "bitwise or '|'";
case AT_BIT_XOR:
return "bitwise xor '^'";
case AT_BIT_NOT:
return "bitwise not '~'";
case ID:
return "identifier '" + token.getText() + "'";
case DECIMAL:
return "decimal '" + token.getText() + "'";
case OCTAL:
return "octal '" + token.getText() + "'";
case HEX:
return "hex '" + token.getText() + "'";
case EOF:
return "end of expression";
default:
return "'" + token.getText() + "'";
}
}
}
// ***********************************************************************
// * Parser Rules
// ***********************************************************************
expression
: conditional EOF!
;
conditional
: logical_or (AT_COND_QUE^ conditional AT_COLON! conditional)?
;
logical_or
: logical_and (AT_BOOL_OR^ logical_and)*
;
logical_and
: bitwise_or (AT_BOOL_AND^ bitwise_or)*
;
bitwise_or
: bitwise_xor (AT_BIT_OR^ bitwise_xor)*
;
bitwise_xor
: bitwise_and (AT_BIT_XOR^ bitwise_and)*
;
bitwise_and
: equality (AT_BIT_AND^ equality)*
;
equality
: relational ((AT_COMP_EQ | AT_COMP_NEQ)^ relational)*
;
relational
: shift ((AT_COMP_LT | AT_COMP_GT | AT_COMP_LTE | AT_COMP_GTE)^ shift)*
;
shift
: additive ((AT_BIT_SHL | AT_BIT_SHR | AT_BIT_SHU)^ additive)*
;
additive
: multiplicative ((AT_ADD | AT_SUBTRACT)^ multiplicative)*
;
multiplicative
: unary ((AT_MULTIPLY | AT_DIVIDE | AT_MODULO)^ unary)*
;
unary
: postfix
| AT_ADD! unary
| unary_operator^ unary
;
unary_operator
: AT_SUBTRACT -> AT_NEGATE
| AT_BIT_NOT
| AT_BOOL_NOT
;
postfix
: primary
| ID arguments -> ^(AT_CALL ID arguments?)
;
primary
: ID
| numeric
| AT_LPAREN! conditional AT_RPAREN!
;
arguments
: AT_LPAREN! (conditional (AT_COMMA! conditional)*)? AT_RPAREN!
;
numeric
: HEX | OCTAL | DECIMAL
;
// ***********************************************************************
// * Lexer Rules
// ***********************************************************************
ID
: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
;
WS
: (' '|'\t'|'\n'|'\r')+ {skip();}
;
DECIMAL
: DECIMALINTEGER AT_DOT DECIMALDIGIT* EXPONENT?
| AT_DOT DECIMALDIGIT+ EXPONENT?
| DECIMALINTEGER EXPONENT?
;
OCTAL
: '0' OCTALDIGIT+
;
HEX
: ('0x'|'0X') HEXDIGIT+
;
fragment
DECIMALINTEGER
: '0'
| '1'..'9' DECIMALDIGIT*
;
fragment
EXPONENT
: ('e'|'E') ('+'|'-')? DECIMALDIGIT+
;
fragment
DECIMALDIGIT
: '0'..'9'
;
fragment
HEXDIGIT
: DECIMALDIGIT
| 'a'..'f'
| 'A'..'F'
;
fragment
OCTALDIGIT
: '0'..'7'
;

View File

@ -0,0 +1,66 @@
AT_ADD=4
AT_BIT_AND=5
AT_BIT_NOT=6
AT_BIT_OR=7
AT_BIT_SHL=8
AT_BIT_SHR=9
AT_BIT_SHU=10
AT_BIT_XOR=11
AT_BOOL_AND=12
AT_BOOL_NOT=13
AT_BOOL_OR=14
AT_CALL=15
AT_COLON=16
AT_COMMA=17
AT_COMP_EQ=18
AT_COMP_GT=19
AT_COMP_GTE=20
AT_COMP_LT=21
AT_COMP_LTE=22
AT_COMP_NEQ=23
AT_COND_QUE=24
AT_DIVIDE=25
AT_DOT=26
AT_LPAREN=27
AT_MODULO=28
AT_MULTIPLY=29
AT_NEGATE=30
AT_RPAREN=31
AT_SUBTRACT=32
DECIMAL=33
DECIMALDIGIT=34
DECIMALINTEGER=35
EXPONENT=36
HEX=37
HEXDIGIT=38
ID=39
OCTAL=40
OCTALDIGIT=41
WS=42
'!'=13
'!='=23
'%'=28
'&&'=12
'&'=5
'('=27
')'=31
'*'=29
'+'=4
','=17
'-'=32
'.'=26
'/'=25
':'=16
'<'=21
'<<'=8
'<='=22
'=='=18
'>'=19
'>='=20
'>>'=9
'>>>'=10
'?'=24
'^'=11
'|'=7
'||'=14
'~'=6

View File

@ -0,0 +1,630 @@
package org.apache.lucene.expressions.js;
/*
* 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.
*/
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.Tree;
import org.apache.lucene.expressions.Expression;
import org.apache.lucene.queries.function.FunctionValues;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import static org.objectweb.asm.Opcodes.AALOAD;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.BIPUSH;
import static org.objectweb.asm.Opcodes.D2I;
import static org.objectweb.asm.Opcodes.D2L;
import static org.objectweb.asm.Opcodes.DADD;
import static org.objectweb.asm.Opcodes.DCMPG;
import static org.objectweb.asm.Opcodes.DCMPL;
import static org.objectweb.asm.Opcodes.DCONST_0;
import static org.objectweb.asm.Opcodes.DCONST_1;
import static org.objectweb.asm.Opcodes.DDIV;
import static org.objectweb.asm.Opcodes.DNEG;
import static org.objectweb.asm.Opcodes.DREM;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DSUB;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.I2D;
import static org.objectweb.asm.Opcodes.I2L;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Opcodes.ICONST_2;
import static org.objectweb.asm.Opcodes.ICONST_3;
import static org.objectweb.asm.Opcodes.ICONST_4;
import static org.objectweb.asm.Opcodes.ICONST_5;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IFGE;
import static org.objectweb.asm.Opcodes.IFGT;
import static org.objectweb.asm.Opcodes.IFLE;
import static org.objectweb.asm.Opcodes.IFLT;
import static org.objectweb.asm.Opcodes.IFNE;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.L2D;
import static org.objectweb.asm.Opcodes.L2I;
import static org.objectweb.asm.Opcodes.LAND;
import static org.objectweb.asm.Opcodes.LCONST_0;
import static org.objectweb.asm.Opcodes.LCONST_1;
import static org.objectweb.asm.Opcodes.LOR;
import static org.objectweb.asm.Opcodes.LSHL;
import static org.objectweb.asm.Opcodes.LSHR;
import static org.objectweb.asm.Opcodes.LUSHR;
import static org.objectweb.asm.Opcodes.LXOR;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.SIPUSH;
import static org.objectweb.asm.Opcodes.V1_7;
/**
* An expression compiler for javascript expressions.
*
* @lucene.experimental
*/
public class JavascriptCompiler {
private static enum ComputedType {
INT, LONG, DOUBLE
}
class Loader extends ClassLoader {
Loader(ClassLoader parent) {
super(parent);
}
public Class<? extends Expression> define(String className, byte[] bytecode) {
return super.defineClass(className, bytecode, 0, bytecode.length).asSubclass(Expression.class);
}
}
private static final String EXPRESSION_CLASS_PREFIX = JavascriptCompiler.class.getPackage().getName() + ".Computed_";
private static final String EXPRESSION_INTERNAL_PREFIX = JavascriptCompiler.class.getPackage().getName().replace(".", "/") + "/Computed_";
private static final String COMPILED_EXPRESSION_INTERNAL = Type.getInternalName(Expression.class);
private static final String FUNCTION_VALUES_INTERNAL = Type.getInternalName(FunctionValues.class);
private Loader loader;
private AtomicLong counter = new AtomicLong();
private String className;
private ClassWriter classWriter;
private MethodVisitor methodVisitor;
private Map<String, Integer> externalsMap;
private List<String> externalsList;
/**
* Constructs a compiler for expressions.
*/
public JavascriptCompiler() {
this(null);
}
/**
* Constructs a compiler for expressions that will be loaded using the given class loader as the parent.
* @param parent Class loader to load the dynamically compiled expression
*/
public JavascriptCompiler(ClassLoader parent) {
if (parent == null) {
parent = getClass().getClassLoader();
}
loader = new Loader(parent);
}
/**
* Compiles the given expression.
*
* @param expression The expression to compile
* @return A new compiled expression
* @throws ParseException on failure to compile
*/
public static Expression compile(String expression) throws ParseException {
return new JavascriptCompiler().compileExpression(expression);
}
/**
* Compiles the given expression.
*
* @param expression The expression to compile
* @return A new compiled expression
* @throws ParseException on failure to compile
*/
public Expression compileExpression(String expression) throws ParseException {
try {
this.className = "Expr" + Long.toString(counter.incrementAndGet());
externalsMap = new HashMap<String, Integer>();
externalsList = new ArrayList<String>();
Tree antlrTree = getAntlrComputedExpressionTree(expression);
beginCompile();
recursiveCompile(antlrTree, ComputedType.DOUBLE);
endCompile();
Class<? extends Expression> evaluatorClass = loader.define(EXPRESSION_CLASS_PREFIX + className, classWriter.toByteArray());
Constructor<? extends Expression> constructor = evaluatorClass.getConstructor(String.class, String[].class);
return constructor.newInstance(expression, externalsList.toArray(new String[externalsList.size()]));
} catch (InstantiationException exception) {
throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
} catch (NoSuchMethodException exception) {
throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
}
}
private void beginCompile() {
classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
classWriter.visit(V1_7, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, EXPRESSION_INTERNAL_PREFIX + className,
null, COMPILED_EXPRESSION_INTERNAL, null);
MethodVisitor constructor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/String;[Ljava/lang/String;)V", null, null);
constructor.visitCode();
constructor.visitVarInsn(ALOAD, 0);
constructor.visitVarInsn(ALOAD, 1);
constructor.visitVarInsn(ALOAD, 2);
constructor.visitMethodInsn(INVOKESPECIAL, COMPILED_EXPRESSION_INTERNAL, "<init>", "(Ljava/lang/String;[Ljava/lang/String;)V");
constructor.visitInsn(RETURN);
constructor.visitMaxs(0, 0);
constructor.visitEnd();
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "evaluate", "(I[L" + FUNCTION_VALUES_INTERNAL + ";)D", null, null);
methodVisitor.visitCode();
}
private void recursiveCompile(Tree current, ComputedType expected) {
int type = current.getType();
String text = current.getText();
switch (type) {
case JavascriptParser.AT_CALL:
Tree identifier = current.getChild(0);
String call = identifier.getText();
int arguments = current.getChildCount() - 1;
JavascriptFunction method = JavascriptFunction.getMethod(call, arguments);
for (int argument = 1; argument <= arguments; ++argument) {
recursiveCompile(current.getChild(argument), ComputedType.DOUBLE);
}
methodVisitor.visitMethodInsn(INVOKESTATIC, method.klass, method.method, method.signature);
typeCompile(expected, ComputedType.DOUBLE);
break;
case JavascriptParser.ID:
int index;
if (externalsMap.containsKey(text)) {
index = externalsMap.get(text);
} else {
index = externalsList.size();
externalsList.add(text);
externalsMap.put(text, index);
}
methodVisitor.visitVarInsn(ALOAD, 2);
switch (index) {
case 0:
methodVisitor.visitInsn(ICONST_0);
break;
case 1:
methodVisitor.visitInsn(ICONST_1);
break;
case 2:
methodVisitor.visitInsn(ICONST_2);
break;
case 3:
methodVisitor.visitInsn(ICONST_3);
break;
case 4:
methodVisitor.visitInsn(ICONST_4);
break;
case 5:
methodVisitor.visitInsn(ICONST_5);
break;
default:
if (index < 128) {
methodVisitor.visitIntInsn(BIPUSH, index);
} else if (index < 16384) {
methodVisitor.visitIntInsn(SIPUSH, index);
} else {
methodVisitor.visitLdcInsn(index);
}
break;
}
methodVisitor.visitInsn(AALOAD);
methodVisitor.visitVarInsn(ILOAD, 1);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, FUNCTION_VALUES_INTERNAL, "doubleVal", "(I)D");
typeCompile(expected, ComputedType.DOUBLE);
break;
case JavascriptParser.HEX:
long hex = Long.parseLong(text.substring(2), 16);
if (expected == ComputedType.INT) {
methodVisitor.visitLdcInsn((int)hex);
} else if (expected == ComputedType.LONG) {
methodVisitor.visitLdcInsn(hex);
} else {
methodVisitor.visitLdcInsn((double)hex);
}
break;
case JavascriptParser.OCTAL:
long octal = Long.parseLong(text.substring(1), 8);
if (expected == ComputedType.INT) {
methodVisitor.visitLdcInsn((int)octal);
} else if (expected == ComputedType.LONG) {
methodVisitor.visitLdcInsn(octal);
} else {
methodVisitor.visitLdcInsn((double)octal);
}
break;
case JavascriptParser.DECIMAL:
double decimal = Double.parseDouble(text);
methodVisitor.visitLdcInsn(decimal);
typeCompile(expected, ComputedType.DOUBLE);
break;
case JavascriptParser.AT_NEGATE:
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
methodVisitor.visitInsn(DNEG);
typeCompile(expected, ComputedType.DOUBLE);
break;
case JavascriptParser.AT_ADD:
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(DADD);
typeCompile(expected, ComputedType.DOUBLE);
break;
case JavascriptParser.AT_SUBTRACT:
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(DSUB);
typeCompile(expected, ComputedType.DOUBLE);
break;
case JavascriptParser.AT_MULTIPLY:
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(Opcodes.DMUL);
typeCompile(expected, ComputedType.DOUBLE);
break;
case JavascriptParser.AT_DIVIDE:
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(DDIV);
typeCompile(expected, ComputedType.DOUBLE);
break;
case JavascriptParser.AT_MODULO:
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(DREM);
typeCompile(expected, ComputedType.DOUBLE);
break;
case JavascriptParser.AT_BIT_SHL:
recursiveCompile(current.getChild(0), ComputedType.LONG);
recursiveCompile(current.getChild(1), ComputedType.INT);
methodVisitor.visitInsn(LSHL);
typeCompile(expected, ComputedType.LONG);
break;
case JavascriptParser.AT_BIT_SHR:
recursiveCompile(current.getChild(0), ComputedType.LONG);
recursiveCompile(current.getChild(1), ComputedType.INT);
methodVisitor.visitInsn(LSHR);
typeCompile(expected, ComputedType.LONG);
break;
case JavascriptParser.AT_BIT_SHU:
recursiveCompile(current.getChild(0), ComputedType.LONG);
recursiveCompile(current.getChild(1), ComputedType.INT);
methodVisitor.visitInsn(LUSHR);
typeCompile(expected, ComputedType.LONG);
break;
case JavascriptParser.AT_BIT_AND:
recursiveCompile(current.getChild(0), ComputedType.LONG);
recursiveCompile(current.getChild(1), ComputedType.LONG);
methodVisitor.visitInsn(LAND);
typeCompile(expected, ComputedType.LONG);
break;
case JavascriptParser.AT_BIT_OR:
recursiveCompile(current.getChild(0), ComputedType.LONG);
recursiveCompile(current.getChild(1), ComputedType.LONG);
methodVisitor.visitInsn(LOR);
typeCompile(expected, ComputedType.LONG);
break;
case JavascriptParser.AT_BIT_XOR:
recursiveCompile(current.getChild(0), ComputedType.LONG);
recursiveCompile(current.getChild(1), ComputedType.LONG);
methodVisitor.visitInsn(LXOR);
typeCompile(expected, ComputedType.LONG);
break;
case JavascriptParser.AT_BIT_NOT:
recursiveCompile(current.getChild(0), ComputedType.LONG);
methodVisitor.visitLdcInsn(new Long(-1));
methodVisitor.visitInsn(LXOR);
typeCompile(expected, ComputedType.LONG);
break;
case JavascriptParser.AT_COMP_EQ:
Label labelEqTrue = new Label();
Label labelEqReturn = new Label();
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(DCMPL);
methodVisitor.visitJumpInsn(IFEQ, labelEqTrue);
truthCompile(expected, false);
methodVisitor.visitJumpInsn(GOTO, labelEqReturn);
methodVisitor.visitLabel(labelEqTrue);
truthCompile(expected, true);
methodVisitor.visitLabel(labelEqReturn);
break;
case JavascriptParser.AT_COMP_NEQ:
Label labelNeqTrue = new Label();
Label labelNeqReturn = new Label();
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(DCMPL);
methodVisitor.visitJumpInsn(IFNE, labelNeqTrue);
truthCompile(expected, false);
methodVisitor.visitJumpInsn(GOTO, labelNeqReturn);
methodVisitor.visitLabel(labelNeqTrue);
truthCompile(expected, true);
methodVisitor.visitLabel(labelNeqReturn);
break;
case JavascriptParser.AT_COMP_LT:
Label labelLtTrue = new Label();
Label labelLtReturn = new Label();
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(DCMPG);
methodVisitor.visitJumpInsn(IFLT, labelLtTrue);
truthCompile(expected, false);
methodVisitor.visitJumpInsn(GOTO, labelLtReturn);
methodVisitor.visitLabel(labelLtTrue);
truthCompile(expected, true);
methodVisitor.visitLabel(labelLtReturn);
break;
case JavascriptParser.AT_COMP_GT:
Label labelGtTrue = new Label();
Label labelGtReturn = new Label();
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(DCMPL);
methodVisitor.visitJumpInsn(IFGT, labelGtTrue);
truthCompile(expected, false);
methodVisitor.visitJumpInsn(GOTO, labelGtReturn);
methodVisitor.visitLabel(labelGtTrue);
truthCompile(expected, true);
methodVisitor.visitLabel(labelGtReturn);
break;
case JavascriptParser.AT_COMP_LTE:
Label labelLteTrue = new Label();
Label labelLteReturn = new Label();
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(DCMPG);
methodVisitor.visitJumpInsn(IFLE, labelLteTrue);
truthCompile(expected, false);
methodVisitor.visitJumpInsn(GOTO, labelLteReturn);
methodVisitor.visitLabel(labelLteTrue);
truthCompile(expected, true);
methodVisitor.visitLabel(labelLteReturn);
break;
case JavascriptParser.AT_COMP_GTE:
Label labelGteTrue = new Label();
Label labelGteReturn = new Label();
recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
methodVisitor.visitInsn(DCMPL);
methodVisitor.visitJumpInsn(IFGE, labelGteTrue);
truthCompile(expected, false);
methodVisitor.visitJumpInsn(GOTO, labelGteReturn);
methodVisitor.visitLabel(labelGteTrue);
truthCompile(expected, true);
methodVisitor.visitLabel(labelGteReturn);
break;
case JavascriptParser.AT_BOOL_NOT:
Label labelNotTrue = new Label();
Label labelNotReturn = new Label();
recursiveCompile(current.getChild(0), ComputedType.INT);
methodVisitor.visitJumpInsn(IFEQ, labelNotTrue);
truthCompile(expected, false);
methodVisitor.visitJumpInsn(GOTO, labelNotReturn);
methodVisitor.visitLabel(labelNotTrue);
truthCompile(expected, true);
methodVisitor.visitLabel(labelNotReturn);
break;
case JavascriptParser.AT_BOOL_AND:
Label andFalse = new Label();
Label andEnd = new Label();
recursiveCompile(current.getChild(0), ComputedType.INT);
methodVisitor.visitJumpInsn(IFEQ, andFalse);
recursiveCompile(current.getChild(1), ComputedType.INT);
methodVisitor.visitJumpInsn(IFEQ, andFalse);
truthCompile(expected, true);
methodVisitor.visitJumpInsn(GOTO, andEnd);
methodVisitor.visitLabel(andFalse);
truthCompile(expected, false);
methodVisitor.visitLabel(andEnd);
break;
case JavascriptParser.AT_BOOL_OR:
Label orTrue = new Label();
Label orEnd = new Label();
recursiveCompile(current.getChild(0), ComputedType.INT);
methodVisitor.visitJumpInsn(IFNE, orTrue);
recursiveCompile(current.getChild(1), ComputedType.INT);
methodVisitor.visitJumpInsn(IFNE, orTrue);
truthCompile(expected, false);
methodVisitor.visitJumpInsn(GOTO, orEnd);
methodVisitor.visitLabel(orTrue);
truthCompile(expected, true);
methodVisitor.visitLabel(orEnd);
break;
case JavascriptParser.AT_COND_QUE:
Label condFalse = new Label();
Label condEnd = new Label();
recursiveCompile(current.getChild(0), ComputedType.INT);
methodVisitor.visitJumpInsn(IFEQ, condFalse);
recursiveCompile(current.getChild(1), expected);
methodVisitor.visitJumpInsn(GOTO, condEnd);
methodVisitor.visitLabel(condFalse);
recursiveCompile(current.getChild(2), expected);
methodVisitor.visitLabel(condEnd);
break;
default:
throw new IllegalStateException("Unknown operation specified: (" + current.getText() + ").");
}
}
private void typeCompile(ComputedType expected, ComputedType actual) {
if (expected == actual) {
return;
}
switch (expected) {
case INT:
if (actual == ComputedType.LONG) {
methodVisitor.visitInsn(L2I);
} else {
methodVisitor.visitInsn(D2I);
}
break;
case LONG:
if (actual == ComputedType.INT) {
methodVisitor.visitInsn(I2L);
} else {
methodVisitor.visitInsn(D2L);
}
break;
default:
if (actual == ComputedType.INT) {
methodVisitor.visitInsn(I2D);
} else {
methodVisitor.visitInsn(L2D);
}
break;
}
}
private void truthCompile(ComputedType expected, boolean truth) {
switch (expected) {
case INT:
methodVisitor.visitInsn(truth ? ICONST_1 : ICONST_0);
break;
case LONG:
methodVisitor.visitInsn(truth ? LCONST_1 : LCONST_0);
break;
default:
methodVisitor.visitInsn(truth ? DCONST_1 : DCONST_0);
break;
}
}
private void endCompile() {
methodVisitor.visitInsn(DRETURN);
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
classWriter.visitEnd();
}
private static Tree getAntlrComputedExpressionTree(String expression) throws ParseException {
CharStream input = new ANTLRStringStream(expression);
JavascriptLexer lexer = new JavascriptLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
JavascriptParser parser = new JavascriptParser(tokens);
try {
return parser.expression().tree;
} catch (RecognitionException exception) {
throw new IllegalArgumentException(exception);
} catch (RuntimeException exception) {
if (exception.getCause() instanceof ParseException) {
throw (ParseException)exception.getCause();
}
throw exception;
}
}
}

View File

@ -0,0 +1,116 @@
package org.apache.lucene.expressions.js;
/*
* 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.
*/
import java.util.HashMap;
import java.util.Map;
import org.apache.lucene.util.MathUtil;
import org.objectweb.asm.Type;
/**
* A wrapper to delegate function calls from a javascript expression.
*/
class JavascriptFunction {
private static Map<String, JavascriptFunction> methods = null;
static {
String mathClass = Type.getInternalName(Math.class);
String mathUtilClass = Type.getInternalName(MathUtil.class);
JavascriptFunction abs = new JavascriptFunction("abs", 1, mathClass, "abs", "(D)D" );
JavascriptFunction acos = new JavascriptFunction("acos", 1, mathClass, "acos", "(D)D" );
JavascriptFunction acosh = new JavascriptFunction("acosh", 1, mathUtilClass, "acosh", "(D)D" );
JavascriptFunction asin = new JavascriptFunction("asin", 1, mathClass, "asin", "(D)D" );
JavascriptFunction asinh = new JavascriptFunction("asinh", 1, mathUtilClass, "asinh", "(D)D" );
JavascriptFunction atan = new JavascriptFunction("atan", 1, mathClass, "atan", "(D)D" );
JavascriptFunction atan2 = new JavascriptFunction("atan2", 1, mathClass, "atan2", "(DD)D");
JavascriptFunction atanh = new JavascriptFunction("atanh", 1, mathUtilClass, "atanh", "(D)D" );
JavascriptFunction ceil = new JavascriptFunction("ceil", 1, mathClass, "ceil", "(D)D" );
JavascriptFunction cos = new JavascriptFunction("cos", 1, mathClass, "cos", "(D)D" );
JavascriptFunction cosh = new JavascriptFunction("cosh", 1, mathClass, "cosh", "(D)D" );
JavascriptFunction exp = new JavascriptFunction("exp", 1, mathClass, "exp", "(D)D" );
JavascriptFunction floor = new JavascriptFunction("floor", 1, mathClass, "floor", "(D)D" );
JavascriptFunction ln = new JavascriptFunction("ln", 1, mathClass, "log", "(D)D" );
JavascriptFunction log10 = new JavascriptFunction("log10", 1, mathClass, "log10", "(D)D" );
JavascriptFunction logn = new JavascriptFunction("logn", 2, mathUtilClass, "log", "(DD)D");
JavascriptFunction pow = new JavascriptFunction("pow", 2, mathClass, "pow", "(DD)D");
JavascriptFunction sin = new JavascriptFunction("sin", 1, mathClass, "sin", "(D)D" );
JavascriptFunction sinh = new JavascriptFunction("sinh", 1, mathClass, "sinh", "(D)D" );
JavascriptFunction sqrt = new JavascriptFunction("sqrt", 1, mathClass, "sqrt", "(D)D" );
JavascriptFunction tan = new JavascriptFunction("tan", 1, mathClass, "tan", "(D)D" );
JavascriptFunction tanh = new JavascriptFunction("tanh", 1, mathClass, "tanh", "(D)D" );
JavascriptFunction min = new JavascriptFunction("min", 2, mathClass, "min", "(DD)D");
JavascriptFunction max = new JavascriptFunction("max", 2, mathClass, "max", "(DD)D");
methods = new HashMap<String, JavascriptFunction>();
methods.put( "abs", abs );
methods.put( "acos", acos );
methods.put( "acosh", acosh );
methods.put( "asin", asin );
methods.put( "asinh", asinh );
methods.put( "atan", atan );
methods.put( "atan2", atan2 );
methods.put( "atanh", atanh );
methods.put( "ceil", ceil );
methods.put( "cos", cos );
methods.put( "cosh", cosh );
methods.put( "exp", exp );
methods.put( "floor", floor );
methods.put( "ln", ln );
methods.put( "log10", log10 );
methods.put( "logn", logn );
methods.put( "max", max );
methods.put( "min", min );
methods.put( "pow", pow );
methods.put( "sin", sin );
methods.put( "sinh", sinh );
methods.put( "sqrt", sqrt );
methods.put( "tan", tan );
methods.put( "tanh", tanh );
}
public static JavascriptFunction getMethod(String call, int arguments) {
JavascriptFunction method = methods.get(call);
if (method == null) {
throw new IllegalArgumentException("Unrecognized method call (" + call + ").");
}
if (arguments != method.arguments && method.arguments != -1) {
throw new IllegalArgumentException("Expected (" + method.arguments + ") arguments for method call (" +
call + "), but found (" + arguments + ").");
}
return method;
}
public final String call;
public final int arguments;
public final String klass;
public final String method;
public final String signature;
private JavascriptFunction(String call, int arguments, String klass, String method, String signature) {
this.call = call;
this.arguments = arguments;
this.klass = klass;
this.method = method;
this.signature = signature;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
<!--
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.
-->
<html>
<head>
<title>Javascript expressions</title>
</head>
<body>
<h1>Javascript expressions</h1>
<p>A Javascript expression is a numeric expression specified using an expression syntax that's based on JavaScript expressions. You can construct expressions using:</p>
<ul>
<li>Integer, floating point, hex and octal literals</li>
<li>Arithmetic operators: <code>+ - * / %</code></li>
<li>Bitwise operators: <code>| &amp; ^ ~ &lt;&lt; &gt;&gt; &gt;&gt;&gt;</code></li>
<li>Boolean operators (including the ternary operator): <code>&& || ! ?:</code></li>
<li>Comparison operators: <code>&lt; &lt;= == &gt;= &gt;</code></li>
<li>Common mathematic functions: <code>abs ceil erf exp floor ln log2 log10 max min sqrt pow</code></li>
<li>Trigonometric library functions: <code>acosh acos asinh asin atanh atan cosh cos sinh sin tanh tan</code></li>
<li>Miscellaneous functions: <code>min, max</code></li>
<li>Arbitrary external variables - see {@link org.apache.lucene.expressions.Bindings}</li>
</ul>
<p>
JavaScript order of precedence rules apply for operators. Shortcut evaluation is used for logical operators—the second argument is only evaluated if the value of the expression cannot be determined after evaluating the first argument. For example, in the expression <code>a || b</code>, <code>b</code> is only evaluated if a is not true.
</p>
<p>
To compile an expression, use {@link org.apache.lucene.expressions.js.JavascriptCompiler}.
</p>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!--
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.
-->
<html>
<head>
<title>expressions</title>
</head>
<body>
<h1>expressions</h1>
<p>
{@link org.apache.lucene.expressions.Expression} - result of compiling an expression, which can
evaluate it for a given document. Each expression can have external variables which evaluate
will retrieve from passed FunctionValues.
</p>
<p>
{@link org.apache.lucene.expressions.Bindings} - abstraction for binding external variables
to a way to get a value for those variables for a particular document (ValueSource)
</p>
<p>
{@link org.apache.lucene.expressions.SimpleBindings} - default implementation of bindings which provide easy ways to bind sort fields and other expressions to external variables
</p>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!--
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.
-->
<html>
<head>
<title>Apache Lucene Expressions Module</title>
</head>
<body>
<h1>The Expressions Module for Apache Lucene</h1>
<p>
The expressions module is new to Lucene 4.6. It provides an API for dynamically computing per-document values based on string expressions.
</p>
<p>
The module is organized in two sections:
<ol>
<li>{@link org.apache.lucene.expressions} - The abstractions and simple utilities for common operations like sorting on an expression</li>
<li>{@link org.apache.lucene.expressions.js} - A compiler for a subset of JavaScript expressions</li>
</ol>
</p>
<p>
For sample code showing how to use the API, see {@link org.apache.lucene.expressions.TestDemoExpressions}.
</p>
<p>
</body>
</html>

View File

@ -0,0 +1,113 @@
package org.apache.lucene.expressions;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.expressions.js.JavascriptCompiler;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.CheckHits;
import org.apache.lucene.search.FieldDoc;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopFieldDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.LuceneTestCase;
/*
* 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.
*/
/** simple demo of using expressions */
public class TestDemoExpressions extends LuceneTestCase {
IndexSearcher searcher;
DirectoryReader reader;
Directory dir;
@Override
public void setUp() throws Exception {
super.setUp();
dir = newDirectory();
RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
Document doc = new Document();
doc.add(newStringField("id", "1", Field.Store.YES));
doc.add(newTextField("body", "some contents and more contents", Field.Store.NO));
doc.add(new NumericDocValuesField("popularity", 5));
iw.addDocument(doc);
doc = new Document();
doc.add(newStringField("id", "2", Field.Store.YES));
doc.add(newTextField("body", "another document with different contents", Field.Store.NO));
doc.add(new NumericDocValuesField("popularity", 20));
iw.addDocument(doc);
doc = new Document();
doc.add(newStringField("id", "3", Field.Store.YES));
doc.add(newTextField("body", "crappy contents", Field.Store.NO));
doc.add(new NumericDocValuesField("popularity", 2));
iw.addDocument(doc);
reader = iw.getReader();
searcher = new IndexSearcher(reader);
iw.close();
}
@Override
public void tearDown() throws Exception {
reader.close();
dir.close();
super.tearDown();
}
/** an example of how to rank by an expression */
public void test() throws Exception {
// compile an expression:
Expression expr = JavascriptCompiler.compile("sqrt(_score) + ln(popularity)");
// we use SimpleBindings: which just maps variables to SortField instances
SimpleBindings bindings = new SimpleBindings();
bindings.add(new SortField("_score", SortField.Type.SCORE));
bindings.add(new SortField("popularity", SortField.Type.INT));
// create a sort field and sort by it (reverse order)
Sort sort = new Sort(expr.getSortField(bindings, true));
Query query = new TermQuery(new Term("body", "contents"));
searcher.search(query, null, 3, sort);
}
/** tests the returned sort values are correct */
public void testSortValues() throws Exception {
Expression expr = JavascriptCompiler.compile("sqrt(_score)");
SimpleBindings bindings = new SimpleBindings();
bindings.add(new SortField("_score", SortField.Type.SCORE));
Sort sort = new Sort(expr.getSortField(bindings, true));
Query query = new TermQuery(new Term("body", "contents"));
TopFieldDocs td = searcher.search(query, null, 3, sort, true, true);
for (int i = 0; i < 3; i++) {
FieldDoc d = (FieldDoc) td.scoreDocs[i];
float expected = (float) Math.sqrt(d.score);
float actual = ((Double)d.fields[0]).floatValue();
assertEquals(expected, actual, CheckHits.explainToleranceDelta(expected, actual));
}
}
}

View File

@ -0,0 +1,127 @@
package org.apache.lucene.expressions;
/*
* 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.
*/
import org.apache.lucene.expressions.js.JavascriptCompiler;
import org.apache.lucene.search.SortField;
import org.apache.lucene.util.LuceneTestCase;
/** Tests validation of bindings */
public class TestExpressionValidation extends LuceneTestCase {
public void testValidExternals() throws Exception {
SimpleBindings bindings = new SimpleBindings();
bindings.add(new SortField("valid0", SortField.Type.INT));
bindings.add(new SortField("valid1", SortField.Type.INT));
bindings.add(new SortField("valid2", SortField.Type.INT));
bindings.add(new SortField("_score", SortField.Type.SCORE));
bindings.add("valide0", JavascriptCompiler.compile("valid0 - valid1 + valid2 + _score"));
bindings.validate();
bindings.add("valide1", JavascriptCompiler.compile("valide0 + valid0"));
bindings.validate();
bindings.add("valide2", JavascriptCompiler.compile("valide0 * valide1"));
bindings.validate();
}
public void testInvalidExternal() throws Exception {
SimpleBindings bindings = new SimpleBindings();
bindings.add(new SortField("valid", SortField.Type.INT));
bindings.add("invalid", JavascriptCompiler.compile("badreference"));
try {
bindings.validate();
fail("didn't get expected exception");
} catch (IllegalArgumentException expected) {
assertTrue(expected.getMessage().contains("Invalid reference"));
}
}
public void testInvalidExternal2() throws Exception {
SimpleBindings bindings = new SimpleBindings();
bindings.add(new SortField("valid", SortField.Type.INT));
bindings.add("invalid", JavascriptCompiler.compile("valid + badreference"));
try {
bindings.validate();
fail("didn't get expected exception");
} catch (IllegalArgumentException expected) {
assertTrue(expected.getMessage().contains("Invalid reference"));
}
}
public void testSelfRecursion() throws Exception {
SimpleBindings bindings = new SimpleBindings();
bindings.add("cycle0", JavascriptCompiler.compile("cycle0"));
try {
bindings.validate();
fail("didn't get expected exception");
} catch (IllegalArgumentException expected) {
assertTrue(expected.getMessage().contains("Cycle detected"));
}
}
public void testCoRecursion() throws Exception {
SimpleBindings bindings = new SimpleBindings();
bindings.add("cycle0", JavascriptCompiler.compile("cycle1"));
bindings.add("cycle1", JavascriptCompiler.compile("cycle0"));
try {
bindings.validate();
fail("didn't get expected exception");
} catch (IllegalArgumentException expected) {
assertTrue(expected.getMessage().contains("Cycle detected"));
}
}
public void testCoRecursion2() throws Exception {
SimpleBindings bindings = new SimpleBindings();
bindings.add("cycle0", JavascriptCompiler.compile("cycle1"));
bindings.add("cycle1", JavascriptCompiler.compile("cycle2"));
bindings.add("cycle2", JavascriptCompiler.compile("cycle0"));
try {
bindings.validate();
fail("didn't get expected exception");
} catch (IllegalArgumentException expected) {
assertTrue(expected.getMessage().contains("Cycle detected"));
}
}
public void testCoRecursion3() throws Exception {
SimpleBindings bindings = new SimpleBindings();
bindings.add("cycle0", JavascriptCompiler.compile("100"));
bindings.add("cycle1", JavascriptCompiler.compile("cycle0 + cycle2"));
bindings.add("cycle2", JavascriptCompiler.compile("cycle0 + cycle1"));
try {
bindings.validate();
fail("didn't get expected exception");
} catch (IllegalArgumentException expected) {
assertTrue(expected.getMessage().contains("Cycle detected"));
}
}
public void testCoRecursion4() throws Exception {
SimpleBindings bindings = new SimpleBindings();
bindings.add("cycle0", JavascriptCompiler.compile("100"));
bindings.add("cycle1", JavascriptCompiler.compile("100"));
bindings.add("cycle2", JavascriptCompiler.compile("cycle1 + cycle0 + cycle3"));
bindings.add("cycle3", JavascriptCompiler.compile("cycle0 + cycle1 + cycle2"));
try {
bindings.validate();
fail("didn't get expected exception");
} catch (IllegalArgumentException expected) {
assertTrue(expected.getMessage().contains("Cycle detected"));
}
}
}

View File

@ -0,0 +1,46 @@
package org.apache.lucene.expressions.js;
/*
* 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.
*/
import java.text.ParseException;
import org.apache.lucene.util.LuceneTestCase;
public class TestJavascriptCompiler extends LuceneTestCase {
public void testValidCompiles() throws Exception {
assertNotNull(JavascriptCompiler.compile("100"));
assertNotNull(JavascriptCompiler.compile("valid0+100"));
assertNotNull(JavascriptCompiler.compile("logn(2, 20+10-5.0)"));
}
public void testInvalidCompiles() throws Exception {
try {
JavascriptCompiler.compile("100 100");
fail();
} catch (ParseException expected) {
// expected exception
}
try {
JavascriptCompiler.compile("7*/-8");
fail();
} catch (ParseException expected) {
// expected exception
}
}
}

View File

@ -0,0 +1,255 @@
package org.apache.lucene.expressions.js;
/*
* 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.
*/
import org.apache.lucene.expressions.Expression;
import org.apache.lucene.util.LuceneTestCase;
public class TestJavascriptFunction extends LuceneTestCase {
private static double DELTA = 0.0000001;
private void testComputedExpressionEvaluation(String name, String expression, double expected) throws Exception {
Expression evaluator = JavascriptCompiler.compile(expression);
double actual = evaluator.evaluate(0, null);
assertEquals(name, expected, actual, DELTA);
}
public void testAbsMethod() throws Exception {
testComputedExpressionEvaluation("abs0", "abs(0)", 0);
testComputedExpressionEvaluation("abs1", "abs(119)", 119);
testComputedExpressionEvaluation("abs2", "abs(119)", 119);
testComputedExpressionEvaluation("abs3", "abs(1)", 1);
testComputedExpressionEvaluation("abs4", "abs(-1)", 1);
}
public void testAcosMethod() throws Exception {
testComputedExpressionEvaluation("acos0", "acos(-1)", Math.PI);
testComputedExpressionEvaluation("acos1", "acos(-0.8660254)", Math.PI*5/6);
testComputedExpressionEvaluation("acos3", "acos(-0.7071068)", Math.PI*3/4);
testComputedExpressionEvaluation("acos4", "acos(-0.5)", Math.PI*2/3);
testComputedExpressionEvaluation("acos5", "acos(0)", Math.PI/2);
testComputedExpressionEvaluation("acos6", "acos(0.5)", Math.PI/3);
testComputedExpressionEvaluation("acos7", "acos(0.7071068)", Math.PI/4);
testComputedExpressionEvaluation("acos8", "acos(0.8660254)", Math.PI/6);
testComputedExpressionEvaluation("acos9", "acos(1)", 0);
}
public void testAcoshMethod() throws Exception {
testComputedExpressionEvaluation("acosh0", "acosh(1)", 0);
testComputedExpressionEvaluation("acosh1", "acosh(2.5)", 1.5667992369724109);
testComputedExpressionEvaluation("acosh2", "acosh(1234567.89)", 14.719378760739708);
}
public void testAsinMethod() throws Exception {
testComputedExpressionEvaluation("asin0", "asin(-1)", -Math.PI/2);
testComputedExpressionEvaluation("asin1", "asin(-0.8660254)", -Math.PI/3);
testComputedExpressionEvaluation("asin3", "asin(-0.7071068)", -Math.PI/4);
testComputedExpressionEvaluation("asin4", "asin(-0.5)", -Math.PI/6);
testComputedExpressionEvaluation("asin5", "asin(0)", 0);
testComputedExpressionEvaluation("asin6", "asin(0.5)", Math.PI/6);
testComputedExpressionEvaluation("asin7", "asin(0.7071068)", Math.PI/4);
testComputedExpressionEvaluation("asin8", "asin(0.8660254)", Math.PI/3);
testComputedExpressionEvaluation("asin9", "asin(1)", Math.PI/2);
}
public void testAsinhMethod() throws Exception {
testComputedExpressionEvaluation("asinh0", "asinh(-1234567.89)", -14.719378760740035);
testComputedExpressionEvaluation("asinh1", "asinh(-2.5)", -1.6472311463710958);
testComputedExpressionEvaluation("asinh2", "asinh(-1)", -0.8813735870195429);
testComputedExpressionEvaluation("asinh3", "asinh(0)", 0);
testComputedExpressionEvaluation("asinh4", "asinh(1)", 0.8813735870195429);
testComputedExpressionEvaluation("asinh5", "asinh(2.5)", 1.6472311463710958);
testComputedExpressionEvaluation("asinh6", "asinh(1234567.89)", 14.719378760740035);
}
public void testAtanMethod() throws Exception {
testComputedExpressionEvaluation("atan0", "atan(-1.732050808)", -Math.PI/3);
testComputedExpressionEvaluation("atan1", "atan(-1)", -Math.PI/4);
testComputedExpressionEvaluation("atan3", "atan(-0.577350269)", -Math.PI/6);
testComputedExpressionEvaluation("atan4", "atan(0)", 0);
testComputedExpressionEvaluation("atan5", "atan(0.577350269)", Math.PI/6);
testComputedExpressionEvaluation("atan6", "atan(1)", Math.PI/4);
testComputedExpressionEvaluation("atan7", "atan(1.732050808)", Math.PI/3);
}
public void testAtanhMethod() throws Exception {
testComputedExpressionEvaluation("atanh0", "atanh(-1)", Double.NEGATIVE_INFINITY);
testComputedExpressionEvaluation("atanh1", "atanh(-0.5)", -0.5493061443340549);
testComputedExpressionEvaluation("atanh2", "atanh(0)", 0);
testComputedExpressionEvaluation("atanh3", "atanh(0.5)", 0.5493061443340549);
testComputedExpressionEvaluation("atanh4", "atanh(1)", Double.POSITIVE_INFINITY);
}
public void testCeilMethod() throws Exception {
testComputedExpressionEvaluation("ceil0", "ceil(0)", 0);
testComputedExpressionEvaluation("ceil1", "ceil(0.1)", 1);
testComputedExpressionEvaluation("ceil2", "ceil(0.9)", 1);
testComputedExpressionEvaluation("ceil3", "ceil(25.2)", 26);
testComputedExpressionEvaluation("ceil4", "ceil(-0.1)", 0);
testComputedExpressionEvaluation("ceil5", "ceil(-0.9)", 0);
testComputedExpressionEvaluation("ceil6", "ceil(-1.1)", -1);
}
public void testCosMethod() throws Exception {
testComputedExpressionEvaluation("cos0", "cos(0)", 1);
testComputedExpressionEvaluation("cos1", "cos(" + Math.PI/2 + ")", 0);
testComputedExpressionEvaluation("cos2", "cos(" + -Math.PI/2 + ")", 0);
testComputedExpressionEvaluation("cos3", "cos(" + Math.PI/4 + ")", 0.7071068);
testComputedExpressionEvaluation("cos4", "cos(" + -Math.PI/4 + ")", 0.7071068);
testComputedExpressionEvaluation("cos5", "cos(" + Math.PI*2/3 + ")",-0.5);
testComputedExpressionEvaluation("cos6", "cos(" + -Math.PI*2/3 + ")", -0.5);
testComputedExpressionEvaluation("cos7", "cos(" + Math.PI/6 + ")", 0.8660254);
testComputedExpressionEvaluation("cos8", "cos(" + -Math.PI/6 + ")", 0.8660254);
}
public void testCoshMethod() throws Exception {
testComputedExpressionEvaluation("cosh0", "cosh(0)", 1);
testComputedExpressionEvaluation("cosh1", "cosh(-1)", 1.5430806348152437);
testComputedExpressionEvaluation("cosh2", "cosh(1)", 1.5430806348152437);
testComputedExpressionEvaluation("cosh3", "cosh(-0.5)", 1.1276259652063807);
testComputedExpressionEvaluation("cosh4", "cosh(0.5)", 1.1276259652063807);
testComputedExpressionEvaluation("cosh5", "cosh(-12.3456789)", 114982.09728671524);
testComputedExpressionEvaluation("cosh6", "cosh(12.3456789)", 114982.09728671524);
}
public void testExpMethod() throws Exception {
testComputedExpressionEvaluation("exp0", "exp(0)", 1);
testComputedExpressionEvaluation("exp1", "exp(-1)", 0.36787944117);
testComputedExpressionEvaluation("exp2", "exp(1)", 2.71828182846);
testComputedExpressionEvaluation("exp3", "exp(-0.5)", 0.60653065971);
testComputedExpressionEvaluation("exp4", "exp(0.5)", 1.6487212707);
testComputedExpressionEvaluation("exp5", "exp(-12.3456789)", 0.0000043485);
testComputedExpressionEvaluation("exp6", "exp(12.3456789)", 229964.194569);
}
public void testFloorMethod() throws Exception {
testComputedExpressionEvaluation("floor0", "floor(0)", 0);
testComputedExpressionEvaluation("floor1", "floor(0.1)", 0);
testComputedExpressionEvaluation("floor2", "floor(0.9)", 0);
testComputedExpressionEvaluation("floor3", "floor(25.2)", 25);
testComputedExpressionEvaluation("floor4", "floor(-0.1)", -1);
testComputedExpressionEvaluation("floor5", "floor(-0.9)", -1);
testComputedExpressionEvaluation("floor6", "floor(-1.1)", -2);
}
public void testLnMethod() throws Exception {
testComputedExpressionEvaluation("ln0", "ln(0)", Double.NEGATIVE_INFINITY);
testComputedExpressionEvaluation("ln1", "ln(" + Math.E + ")", 1);
testComputedExpressionEvaluation("ln2", "ln(-1)", Double.NaN);
testComputedExpressionEvaluation("ln3", "ln(1)", 0);
testComputedExpressionEvaluation("ln4", "ln(0.5)", -0.69314718056);
testComputedExpressionEvaluation("ln5", "ln(12.3456789)", 2.51330611521);
}
public void testLog10Method() throws Exception {
testComputedExpressionEvaluation("log100", "log10(0)", Double.NEGATIVE_INFINITY);
testComputedExpressionEvaluation("log101", "log10(1)", 0);
testComputedExpressionEvaluation("log102", "log10(-1)", Double.NaN);
testComputedExpressionEvaluation("log103", "log10(0.5)", -0.3010299956639812);
testComputedExpressionEvaluation("log104", "log10(12.3456789)", 1.0915149771692705);
}
public void testLognMethod() throws Exception {
testComputedExpressionEvaluation("logn0", "logn(2, 0)", Double.NEGATIVE_INFINITY);
testComputedExpressionEvaluation("logn1", "logn(2, 1)", 0);
testComputedExpressionEvaluation("logn2", "logn(2, -1)", Double.NaN);
testComputedExpressionEvaluation("logn3", "logn(2, 0.5)", -1);
testComputedExpressionEvaluation("logn4", "logn(2, 12.3456789)", 3.6259342686489378);
testComputedExpressionEvaluation("logn5", "logn(2.5, 0)", Double.NEGATIVE_INFINITY);
testComputedExpressionEvaluation("logn6", "logn(2.5, 1)", 0);
testComputedExpressionEvaluation("logn7", "logn(2.5, -1)", Double.NaN);
testComputedExpressionEvaluation("logn8", "logn(2.5, 0.5)", -0.75647079736603);
testComputedExpressionEvaluation("logn9", "logn(2.5, 12.3456789)", 2.7429133874016745);
}
public void testMaxMethod() throws Exception {
testComputedExpressionEvaluation("max0", "max(0, 0)", 0);
testComputedExpressionEvaluation("max1", "max(1, 0)", 1);
testComputedExpressionEvaluation("max2", "max(0, -1)", 0);
testComputedExpressionEvaluation("max3", "max(-1, 0)", 0);
testComputedExpressionEvaluation("max4", "max(25, 23)", 25);
}
public void testMinMethod() throws Exception {
testComputedExpressionEvaluation("min0", "min(0, 0)", 0);
testComputedExpressionEvaluation("min1", "min(1, 0)", 0);
testComputedExpressionEvaluation("min2", "min(0, -1)", -1);
testComputedExpressionEvaluation("min3", "min(-1, 0)", -1);
testComputedExpressionEvaluation("min4", "min(25, 23)", 23);
}
public void testPowMethod() throws Exception {
testComputedExpressionEvaluation("pow0", "pow(0, 0)", 1);
testComputedExpressionEvaluation("pow1", "pow(0.1, 2)", 0.01);
testComputedExpressionEvaluation("pow2", "pow(0.9, -1)", 1.1111111111111112);
testComputedExpressionEvaluation("pow3", "pow(2.2, -2.5)", 0.13929749224447147);
testComputedExpressionEvaluation("pow4", "pow(5, 3)", 125);
testComputedExpressionEvaluation("pow5", "pow(-0.9, 5)", -0.59049);
testComputedExpressionEvaluation("pow6", "pow(-1.1, 2)", 1.21);
}
public void testSinMethod() throws Exception {
testComputedExpressionEvaluation("sin0", "sin(0)", 0);
testComputedExpressionEvaluation("sin1", "sin(" + Math.PI/2 + ")", 1);
testComputedExpressionEvaluation("sin2", "sin(" + -Math.PI/2 + ")", -1);
testComputedExpressionEvaluation("sin3", "sin(" + Math.PI/4 + ")", 0.7071068);
testComputedExpressionEvaluation("sin4", "sin(" + -Math.PI/4 + ")", -0.7071068);
testComputedExpressionEvaluation("sin5", "sin(" + Math.PI*2/3 + ")", 0.8660254);
testComputedExpressionEvaluation("sin6", "sin(" + -Math.PI*2/3 + ")", -0.8660254);
testComputedExpressionEvaluation("sin7", "sin(" + Math.PI/6 + ")", 0.5);
testComputedExpressionEvaluation("sin8", "sin(" + -Math.PI/6 + ")", -0.5);
}
public void testSinhMethod() throws Exception {
testComputedExpressionEvaluation("sinh0", "sinh(0)", 0);
testComputedExpressionEvaluation("sinh1", "sinh(-1)", -1.1752011936438014);
testComputedExpressionEvaluation("sinh2", "sinh(1)", 1.1752011936438014);
testComputedExpressionEvaluation("sinh3", "sinh(-0.5)", -0.52109530549);
testComputedExpressionEvaluation("sinh4", "sinh(0.5)", 0.52109530549);
testComputedExpressionEvaluation("sinh5", "sinh(-12.3456789)", -114982.09728236674);
testComputedExpressionEvaluation("sinh6", "sinh(12.3456789)", 114982.09728236674);
}
public void testSqrtMethod() throws Exception {
testComputedExpressionEvaluation("sqrt0", "sqrt(0)", 0);
testComputedExpressionEvaluation("sqrt1", "sqrt(-1)", Double.NaN);
testComputedExpressionEvaluation("sqrt2", "sqrt(0.49)", 0.7);
testComputedExpressionEvaluation("sqrt3", "sqrt(49)", 7);
}
public void testTanMethod() throws Exception {
testComputedExpressionEvaluation("tan0", "tan(0)", 0);
testComputedExpressionEvaluation("tan1", "tan(-1)", -1.55740772465);
testComputedExpressionEvaluation("tan2", "tan(1)", 1.55740772465);
testComputedExpressionEvaluation("tan3", "tan(-0.5)", -0.54630248984);
testComputedExpressionEvaluation("tan4", "tan(0.5)", 0.54630248984);
testComputedExpressionEvaluation("tan1", "tan(-1.3)", -3.60210244797);
testComputedExpressionEvaluation("tan2", "tan(1.3)", 3.60210244797);
}
public void testTanhMethod() throws Exception {
testComputedExpressionEvaluation("tanh0", "tanh(0)", 0);
testComputedExpressionEvaluation("tanh1", "tanh(-1)", -0.76159415595);
testComputedExpressionEvaluation("tanh2", "tanh(1)", 0.76159415595);
testComputedExpressionEvaluation("tanh3", "tanh(-0.5)", -0.46211715726);
testComputedExpressionEvaluation("tanh4", "tanh(0.5)", 0.46211715726);
testComputedExpressionEvaluation("tanh5", "tanh(-12.3456789)", -0.99999999996);
testComputedExpressionEvaluation("tanh6", "tanh(12.3456789)", 0.99999999996);
}
}

View File

@ -0,0 +1,313 @@
package org.apache.lucene.expressions.js;
/*
* 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.
*/
import org.apache.lucene.expressions.Expression;
import org.apache.lucene.util.LuceneTestCase;
public class TestJavascriptOperations extends LuceneTestCase {
private void testComputedExpression(String name, String expression, long expected) throws Exception {
Expression evaluator = JavascriptCompiler.compile(expression);
long actual = (long)evaluator.evaluate(0, null);
assertEquals(name, expected, actual);
}
public void testNegationOperation() throws Exception {
testComputedExpression("negate0", "-1", -1);
testComputedExpression("negate1", "--1", 1);
testComputedExpression("negate1", "-(-1)", 1);
testComputedExpression("negate2", "-0", 0);
testComputedExpression("negate3", "--0", 0);
}
public void testAddOperation() throws Exception {
testComputedExpression("add0", "1+1", 2);
testComputedExpression("add1", "1+0.5+0.5", 2);
testComputedExpression("add2", "5+10", 15);
testComputedExpression("add3", "1+1+2", 4);
testComputedExpression("add4", "(1+1)+2", 4);
testComputedExpression("add5", "1+(1+2)", 4);
testComputedExpression("add6", "0+1", 1);
testComputedExpression("add7", "1+0", 1);
testComputedExpression("add8", "0+0", 0);
}
public void testSubtractOperation() throws Exception {
testComputedExpression("subtract0", "1-1", 0);
testComputedExpression("subtract1", "5-10", -5);
testComputedExpression("subtract2", "1-0.5-0.5", 0);
testComputedExpression("subtract3", "1-1-2", -2);
testComputedExpression("subtract4", "(1-1)-2", -2);
testComputedExpression("subtract5", "1-(1-2)", 2);
testComputedExpression("subtract6", "0-1", -1);
testComputedExpression("subtract6", "1-0", 1);
testComputedExpression("subtract6", "0-0", 0);
}
public void testMultiplyOperation() throws Exception {
testComputedExpression("multiply0", "1*1", 1);
testComputedExpression("multiply1", "5*10", 50);
testComputedExpression("multiply2", "50*0.1", 5);
testComputedExpression("multiply4", "1*1*2", 2);
testComputedExpression("multiply4", "(1*1)*2", 2);
testComputedExpression("multiply5", "1*(1*2)", 2);
testComputedExpression("multiply6", "10*0", 0);
testComputedExpression("multiply6", "0*0", 0);
}
public void testDivisionOperation() throws Exception {
testComputedExpression("division0", "1*1", 1);
testComputedExpression("division1", "10/5", 2);
testComputedExpression("division2", "10/0.5", 20);
testComputedExpression("division3", "10/5/2", 1);
testComputedExpression("division4", "(27/9)/3", 1);
testComputedExpression("division5", "27/(9/3)", 9);
testComputedExpression("division6", "1/0", 9223372036854775807L);
}
public void testModuloOperation() throws Exception {
testComputedExpression("modulo0", "1%1", 0);
testComputedExpression("modulo1", "10%3", 1);
testComputedExpression("modulo2", "10%3%2", 1);
testComputedExpression("modulo3", "(27%10)%4", 3);
testComputedExpression("modulo4", "27%(9%5)", 3);
}
public void testLessThanOperation() throws Exception {
testComputedExpression("lessthan0", "1 < 1", 0);
testComputedExpression("lessthan1", "2 < 1", 0);
testComputedExpression("lessthan2", "1 < 2", 1);
testComputedExpression("lessthan3", "2 < 1 < 3", 1);
testComputedExpression("lessthan4", "2 < (1 < 3)", 0);
testComputedExpression("lessthan5", "(2 < 1) < 1", 1);
testComputedExpression("lessthan6", "-1 < -2", 0);
testComputedExpression("lessthan7", "-1 < 0", 1);
}
public void testLessThanEqualsOperation() throws Exception {
testComputedExpression("lessthanequals0", "1 <= 1", 1);
testComputedExpression("lessthanequals1", "2 <= 1", 0);
testComputedExpression("lessthanequals2", "1 <= 2", 1);
testComputedExpression("lessthanequals3", "1 <= 1 <= 0", 0);
testComputedExpression("lessthanequals4", "-1 <= -1", 1);
testComputedExpression("lessthanequals5", "-1 <= 0", 1);
testComputedExpression("lessthanequals6", "-1 <= -2", 0);
testComputedExpression("lessthanequals7", "-1 <= 0", 1);
}
public void testGreaterThanOperation() throws Exception {
testComputedExpression("greaterthan0", "1 > 1", 0);
testComputedExpression("greaterthan1", "2 > 1", 1);
testComputedExpression("greaterthan2", "1 > 2", 0);
testComputedExpression("greaterthan3", "2 > 1 > 3", 0);
testComputedExpression("greaterthan4", "2 > (1 > 3)", 1);
testComputedExpression("greaterthan5", "(2 > 1) > 1", 0);
testComputedExpression("greaterthan6", "-1 > -2", 1);
testComputedExpression("greaterthan7", "-1 > 0", 0);
}
public void testGreaterThanEqualsOperation() throws Exception {
testComputedExpression("greaterthanequals0", "1 >= 1", 1);
testComputedExpression("greaterthanequals1", "2 >= 1", 1);
testComputedExpression("greaterthanequals2", "1 >= 2", 0);
testComputedExpression("greaterthanequals3", "1 >= 1 >= 0", 1);
testComputedExpression("greaterthanequals4", "-1 >= -1", 1);
testComputedExpression("greaterthanequals5", "-1 >= 0", 0);
testComputedExpression("greaterthanequals6", "-1 >= -2", 1);
testComputedExpression("greaterthanequals7", "-1 >= 0", 0);
}
public void testEqualsOperation() throws Exception {
testComputedExpression("equals0", "1 == 1", 1);
testComputedExpression("equals1", "0 == 0", 1);
testComputedExpression("equals2", "-1 == -1", 1);
testComputedExpression("equals3", "1.1 == 1.1", 1);
testComputedExpression("equals4", "0.9 == 0.9", 1);
testComputedExpression("equals5", "-0 == 0", 1);
testComputedExpression("equals6", "0 == 1", 0);
testComputedExpression("equals7", "1 == 2", 0);
testComputedExpression("equals8", "-1 == 1", 0);
testComputedExpression("equals9", "-1 == 0", 0);
testComputedExpression("equals10", "-2 == 1", 0);
testComputedExpression("equals11", "-2 == -1", 0);
}
public void testNotEqualsOperation() throws Exception {
testComputedExpression("notequals0", "1 != 1", 0);
testComputedExpression("notequals1", "0 != 0", 0);
testComputedExpression("notequals2", "-1 != -1", 0);
testComputedExpression("notequals3", "1.1 != 1.1", 0);
testComputedExpression("notequals4", "0.9 != 0.9", 0);
testComputedExpression("notequals5", "-0 != 0", 0);
testComputedExpression("notequals6", "0 != 1", 1);
testComputedExpression("notequals7", "1 != 2", 1);
testComputedExpression("notequals8", "-1 != 1", 1);
testComputedExpression("notequals9", "-1 != 0", 1);
testComputedExpression("notequals10", "-2 != 1", 1);
testComputedExpression("notequals11", "-2 != -1", 1);
}
public void testBoolNotOperation() throws Exception {
testComputedExpression("boolnot0", "!1", 0);
testComputedExpression("boolnot0", "!!1", 1);
testComputedExpression("boolnot0", "!0", 1);
testComputedExpression("boolnot0", "!!0", 0);
testComputedExpression("boolnot0", "!-1", 0);
testComputedExpression("boolnot0", "!2", 0);
testComputedExpression("boolnot0", "!-2", 0);
}
public void testBoolAndOperation() throws Exception {
testComputedExpression("booland0", "1 && 1", 1);
testComputedExpression("booland1", "1 && 0", 0);
testComputedExpression("booland2", "0 && 1", 0);
testComputedExpression("booland3", "0 && 0", 0);
testComputedExpression("booland4", "-1 && -1", 1);
testComputedExpression("booland5", "-1 && 0", 0);
testComputedExpression("booland6", "0 && -1", 0);
testComputedExpression("booland7", "-0 && -0", 0);
}
public void testBoolOrOperation() throws Exception {
testComputedExpression("boolor0", "1 || 1", 1);
testComputedExpression("boolor1", "1 || 0", 1);
testComputedExpression("boolor2", "0 || 1", 1);
testComputedExpression("boolor3", "0 || 0", 0);
testComputedExpression("boolor4", "-1 || -1", 1);
testComputedExpression("boolor5", "-1 || 0", 1);
testComputedExpression("boolor6", "0 || -1", 1);
testComputedExpression("boolor7", "-0 || -0", 0);
}
public void testConditionalOperation() throws Exception {
testComputedExpression("conditional0", "1 ? 2 : 3", 2);
testComputedExpression("conditional1", "-1 ? 2 : 3", 2);
testComputedExpression("conditional2", "0 ? 2 : 3", 3);
testComputedExpression("conditional3", "1 ? 2 ? 3 : 4 : 5", 3);
testComputedExpression("conditional4", "0 ? 2 ? 3 : 4 : 5", 5);
testComputedExpression("conditional5", "1 ? 0 ? 3 : 4 : 5", 4);
testComputedExpression("conditional6", "1 ? 2 : 3 ? 4 : 5", 2);
testComputedExpression("conditional7", "0 ? 2 : 3 ? 4 : 5", 4);
testComputedExpression("conditional8", "0 ? 2 : 0 ? 4 : 5", 5);
testComputedExpression("conditional9", "(1 ? 1 : 0) ? 3 : 4", 3);
testComputedExpression("conditional10", "(0 ? 1 : 0) ? 3 : 4", 4);
}
public void testBitShiftLeft() throws Exception {
testComputedExpression("bitshiftleft0", "1 << 1", 2);
testComputedExpression("bitshiftleft1", "2 << 1", 4);
testComputedExpression("bitshiftleft2", "-1 << 31", -2147483648);
testComputedExpression("bitshiftleft3", "3 << 5", 96);
testComputedExpression("bitshiftleft4", "-5 << 3", -40);
testComputedExpression("bitshiftleft5", "4195 << 7", 536960);
testComputedExpression("bitshiftleft6", "4195 << 66", 16780);
testComputedExpression("bitshiftleft7", "4195 << 6", 268480);
testComputedExpression("bitshiftleft8", "4195 << 70", 268480);
testComputedExpression("bitshiftleft9", "-4195 << 70", -268480);
testComputedExpression("bitshiftleft10", "-15 << 62", 4611686018427387904L);
}
public void testBitShiftRight() throws Exception {
testComputedExpression("bitshiftright0", "1 >> 1", 0);
testComputedExpression("bitshiftright1", "2 >> 1", 1);
testComputedExpression("bitshiftright2", "-1 >> 5", -1);
testComputedExpression("bitshiftright3", "-2 >> 30", -1);
testComputedExpression("bitshiftright4", "-5 >> 1", -3);
testComputedExpression("bitshiftright5", "536960 >> 7", 4195);
testComputedExpression("bitshiftright6", "16780 >> 66", 4195);
testComputedExpression("bitshiftright7", "268480 >> 6", 4195);
testComputedExpression("bitshiftright8", "268480 >> 70", 4195);
testComputedExpression("bitshiftright9", "-268480 >> 70", -4195);
testComputedExpression("bitshiftright10", "-2147483646 >> 1", -1073741823);
}
public void testBitShiftRightUnsigned() throws Exception {
testComputedExpression("bitshiftrightunsigned0", "1 >>> 1", 0);
testComputedExpression("bitshiftrightunsigned1", "2 >>> 1", 1);
testComputedExpression("bitshiftrightunsigned2", "-1 >>> 37", 134217727);
testComputedExpression("bitshiftrightunsigned3", "-2 >>> 62", 3);
testComputedExpression("bitshiftrightunsigned4", "-5 >>> 33", 2147483647);
testComputedExpression("bitshiftrightunsigned5", "536960 >>> 7", 4195);
testComputedExpression("bitshiftrightunsigned6", "16780 >>> 66", 4195);
testComputedExpression("bitshiftrightunsigned7", "268480 >>> 6", 4195);
testComputedExpression("bitshiftrightunsigned8", "268480 >>> 70", 4195);
testComputedExpression("bitshiftrightunsigned9", "-268480 >>> 102", 67108863);
testComputedExpression("bitshiftrightunsigned10", "2147483648 >>> 1", 1073741824);
}
public void testBitwiseAnd() throws Exception {
testComputedExpression("bitwiseand0", "4 & 4", 4);
testComputedExpression("bitwiseand1", "3 & 2", 2);
testComputedExpression("bitwiseand2", "7 & 3", 3);
testComputedExpression("bitwiseand3", "-1 & -1", -1);
testComputedExpression("bitwiseand4", "-1 & 25", 25);
testComputedExpression("bitwiseand5", "3 & 7", 3);
testComputedExpression("bitwiseand6", "0 & 1", 0);
testComputedExpression("bitwiseand7", "1 & 0", 0);
}
public void testBitwiseOr() throws Exception {
testComputedExpression("bitwiseor0", "4 | 4", 4);
testComputedExpression("bitwiseor1", "5 | 2", 7);
testComputedExpression("bitwiseor2", "7 | 3", 7);
testComputedExpression("bitwiseor3", "-1 | -5", -1);
testComputedExpression("bitwiseor4", "-1 | 25", -1);
testComputedExpression("bitwiseor5", "-100 | 15", -97);
testComputedExpression("bitwiseor6", "0 | 1", 1);
testComputedExpression("bitwiseor7", "1 | 0", 1);
}
public void testBitwiseXor() throws Exception {
testComputedExpression("bitwisexor0", "4 ^ 4", 0);
testComputedExpression("bitwisexor1", "5 ^ 2", 7);
testComputedExpression("bitwisexor2", "15 ^ 3", 12);
testComputedExpression("bitwisexor3", "-1 ^ -5", 4);
testComputedExpression("bitwisexor4", "-1 ^ 25", -26);
testComputedExpression("bitwisexor5", "-100 ^ 15", -109);
testComputedExpression("bitwisexor6", "0 ^ 1", 1);
testComputedExpression("bitwisexor7", "1 ^ 0", 1);
testComputedExpression("bitwisexor8", "0 ^ 0", 0);
}
public void testBitwiseNot() throws Exception {
testComputedExpression("bitwisenot0", "~-5", 4);
testComputedExpression("bitwisenot1", "~25", -26);
testComputedExpression("bitwisenot2", "~0", -1);
testComputedExpression("bitwisenot3", "~-1", 0);
}
public void testDecimalConst() throws Exception {
testComputedExpression("decimalconst0", "0", 0);
testComputedExpression("decimalconst1", "1", 1);
testComputedExpression("decimalconst2", "123456789", 123456789);
testComputedExpression("decimalconst3", "5.6E2", 560);
}
public void testHexConst() throws Exception {
testComputedExpression("hexconst0", "0x0", 0);
testComputedExpression("hexconst1", "0x1", 1);
testComputedExpression("hexconst2", "0xF", 15);
testComputedExpression("hexconst3", "0x1234ABCDEF", 78193085935L);
}
public void testOctalConst() throws Exception {
testComputedExpression("octalconst0", "00", 0);
testComputedExpression("octalconst1", "01", 1);
testComputedExpression("octalconst2", "010", 8);
testComputedExpression("octalconst3", "0123456777", 21913087);
}
}

View File

@ -0,0 +1 @@
7797e70e3f5cb4229695f08ff50333266cf81125

View File

@ -0,0 +1,7 @@
Copyright (c) 2012 Terence Parr and Sam Harwell
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
ad568238ee36a820bd6c6806807e8a14ea34684d

View File

@ -0,0 +1,26 @@
Copyright (c) 2012 France Télécom
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1 @@

View File

@ -433,6 +433,28 @@
<property name="codecs-javadocs.uptodate" value="true"/>
</target>
<property name="expressions.jar" value="${common.dir}/build/expressions/lucene-expressions-${version}.jar"/>
<target name="check-expressions-uptodate" unless="expressions.uptodate">
<module-uptodate name="expressions" jarfile="${expressions.jar}" property="expressions.uptodate"/>
</target>
<target name="jar-expressions" unless="expressions.uptodate" depends="check-expressions-uptodate">
<ant dir="${common.dir}/expressions" target="jar-core" inheritAll="false">
<propertyset refid="uptodate.and.compiled.properties"/>
</ant>
<property name="expressions.uptodate" value="true"/>
</target>
<property name="expressions-javadoc.jar" value="${common.dir}/build/expressions/lucene-expressions-${version}-javadoc.jar"/>
<target name="check-expressions-javadocs-uptodate" unless="expressions-javadocs.uptodate">
<module-uptodate name="expressions" jarfile="${expressions-javadoc.jar}" property="expressions-javadocs.uptodate"/>
</target>
<target name="javadocs-expressions" unless="expressions-javadocs.uptodate" depends="check-expressions-javadocs-uptodate">
<ant dir="${common.dir}/expressions" target="javadocs" inheritAll="false">
<propertyset refid="uptodate.and.compiled.properties"/>
</ant>
<property name="expressions-javadocs.uptodate" value="true"/>
</target>
<property name="grouping.jar" value="${common.dir}/build/grouping/lucene-grouping-${version}.jar"/>
<target name="check-grouping-uptodate" unless="grouping.uptodate">
<module-uptodate name="grouping" jarfile="${grouping.jar}" property="grouping.uptodate"/>