package org.apache.solr.search.xjoin;
/*
* 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.Iterator;
public class JoinSpec<T extends Comparable<T>> {
private enum Op {
xjoin, unary, or, and, xor, and_not
}
Op operator = Op.unary;
JoinSpec<T> first, second; // both are be null when operator == xjoin
String componentName; // only non-null when operator == xjoin
JoinSpec<T> parent;
private JoinSpec(JoinSpec<T> parent) {
this.parent = parent;
}
private void add(JoinSpec<T> child) {
if (first == null) {
first = child;
} else if (second == null) {
second = child;
} else {
throw new RuntimeException("Bad operator (programming error)");
}
}
public Iterator<T> iterator(Iterable it) {
if (operator == Op.xjoin) {
return it.iterator(componentName);
}
Iterator<T> first = this.first.iterator(it);
if (operator == Op.unary) {
return first;
}
Iterator<T> second = this.second.iterator(it);
switch (operator) {
case or:
return Combinations.or(first, second);
case and:
return Combinations.and(first, second);
case xor:
return Combinations.xor(first, second);
case and_not:
return Combinations.andNot(first, second);
default:
throw new RuntimeException("Bad operator: " + operator);
}
}
public static <T extends Comparable<T>> JoinSpec<T> parse(String v) {
// ((a OR b) AND c) XOR (d AND NOT e)
JoinSpec<T> spec = new JoinSpec<>(null);
for (int i = 0; i < v.length(); ++i) {
char c = v.charAt(i);
if (Character.isWhitespace(c)) {
continue;
}
int z = 0;
if (c == '(') {
JoinSpec<T> js = new JoinSpec<>(spec);
spec.add(js);
spec = js;
} else if (c == ')') {
spec = spec.parent;
} else if ((z = safeCmp(v, i, "OR")) > 0) {
spec.operator = Op.or;
} else if ((z = safeCmp(v, i, "AND NOT")) > 0) {
spec.operator = Op.and_not;
} else if ((z = safeCmp(v, i, "AND")) > 0) {
spec.operator = Op.and;
} else if ((z = safeCmp(v, i, "XOR")) > 0) {
spec.operator = Op.xor;
} else {
// it must be a component name until the next whitespace or )
//FIXME probably need a cunning while to avoid end-of-input failures
int j;
for (j = i; j < v.length(); ++j) {
char cc = v.charAt(j);
if (cc == '(') {
//FIXME use a SyntaxError exception class
throw new RuntimeException("Syntax error");
}
if (Character.isWhitespace(cc) || cc == ')') {
break;
}
}
JoinSpec<T> js = new JoinSpec<>(spec);
js.operator = Op.xjoin;
js.componentName = v.substring(i, j);
spec.add(js);
i = j - 1;
}
i += z;
}
return spec;
}
private static int safeCmp(String v, int i, String t) {
int j = i + t.length();
boolean match = j < v.length() && v.substring(i, j).equals(t);
return match ? t.length() : 0;
}
public interface Iterable {
<T extends Comparable<T>> Iterator<T> iterator(String componentName);
}
}