blob: ba90a77fb4761bf1f840414aca78ddcfb8fed222 [file] [log] [blame]
package de.ids_mannheim.korap.query;
import de.ids_mannheim.korap.util.QueryException;
import org.apache.lucene.search.spans.Spans;
import java.util.*;
/**
* Class representing a frame constraint, as used by
* {@link SpanWithinQuery}.
* Multiple constraints are represented as a bit vector,
* supporting fast checks if a certain condition is met.
* The following distinctive frame conditions are supported:
*
* <dl>
* <dt>precedes</dt>
* <dd>A precedes B</dd>
*
* <dt>precedesDirectly</dt>
* <dd>A precedes B directly</dd>
*
* <dt>overlapsLeft</dt>
* <dd>A overlaps B to the left</dd>
*
* <dt>alignsLeft</dt>
* <dd>A aligns with B to the left</dd>
*
* <dt>startsWith</dt>
* <dd>A starts with B</dd>
*
* <dt>matches</dt>
* <dd>A matches B</dd>
*
* <dt>isWithin</dt>
* <dd>A is within B</dd>
*
* <dt>isAround</dt>
* <dd>A is around B</dd>
*
* <dt>endsWith</dt>
* <dd>A ends with B</dd>
*
* <dt>alignsRight</dt>
* <dd>A aligns with B to the right</dd>
*
* <dt>overlapsRight</dt>
* <dd>A overlaps B to the right</dd>
*
* <dt>succeedsDirectly</dt>
* <dd>A succeeds B directly</dd>
*
* <dt>succeeds</dt>
* <dd>A succeeds B</dd>
* </dl>
*
* @author diewald
*/
public class FrameConstraint {
public static final int PRECEDES = 1, PRECEDES_DIRECTLY = 1 << 1,
OVERLAPS_LEFT = 1 << 2, ALIGNS_LEFT = 1 << 3, STARTS_WITH = 1 << 4,
MATCHES = 1 << 5, IS_WITHIN = 1 << 6, IS_AROUND = 1 << 7,
ENDS_WITH = 1 << 8, ALIGNS_RIGHT = 1 << 9, OVERLAPS_RIGHT = 1 << 10,
SUCCEEDS_DIRECTLY = 1 << 11, SUCCEEDS = 1 << 12, ALL = 1024 * 8 - 1;
private static final Map<String, Integer> FRAME;
private static final List<Integer> NEXT_B;
static {
Map<String, Integer> FRAME_t = new HashMap<>();
List<Integer> NEXT_B_t = new ArrayList(16);
/*
* A precedes B
*
* |-A-|
* |-B-|
*
* A.end < B.start
*/
FRAME_t.put("precedes", PRECEDES);
NEXT_B_t.add(PRECEDES);
/*
* A precedes B directly
*
* |-A-|
* |-B-|
*
* A.end == b.start
*/
FRAME_t.put("precedesDirectly", PRECEDES_DIRECTLY);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY);
/*
* A overlaps B to the left
*
* |-A-|
* |-B-|
*
* a.end < b.end && a.end > b.start && a.start < b.start
*/
FRAME_t.put("overlapsLeft", OVERLAPS_LEFT);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY | OVERLAPS_LEFT);
/*
* A aligns B to the left
*
* |-A-|
* |-B--|
*
* a.end < b.end && a.start == b.start
*/
FRAME_t.put("alignsLeft", ALIGNS_LEFT);
NEXT_B_t.add(
PRECEDES | PRECEDES_DIRECTLY | OVERLAPS_LEFT | ALIGNS_LEFT);
/*
* A starts with B
*
* |-A--|
* |-B-|
*
* a.end > b.end && a.start == b.start
*/
FRAME_t.put("startsWith", STARTS_WITH);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY | OVERLAPS_LEFT | ALIGNS_LEFT
| STARTS_WITH | MATCHES | IS_AROUND | ENDS_WITH);
/*
* A matches B
*
* |-A-|
* |-B-|
*
* a.end = b.end && a.start = b.start
*/
FRAME_t.put("matches", MATCHES);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY | OVERLAPS_LEFT | ALIGNS_LEFT
| MATCHES | ENDS_WITH);
/*
* A is within B
*
* |-A-|
* |--B--|
*
* a.end < b.end && a.start > b.start
*/
FRAME_t.put("isWithin", IS_WITHIN);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY | ALIGNS_LEFT | IS_WITHIN);
/*
* A is around B
*
* |--A--|
* |-B-|
*
* a.start < b.start && a.end > b.end
*/
FRAME_t.put("isAround", IS_AROUND);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY | OVERLAPS_LEFT | IS_AROUND
| ENDS_WITH);
/*
* A ends with B
*
* |-A--|
* |-B-|
*
* a.start < b.start && a.end == b.end
*/
FRAME_t.put("endsWith", ENDS_WITH);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY | OVERLAPS_LEFT | ENDS_WITH);
/*
* A aligns B to the right
*
* |-A-|
* |--B-|
*
* a.start > b.start && a.end == b.end
*/
FRAME_t.put("alignsRight", ALIGNS_RIGHT);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY | ALIGNS_LEFT | MATCHES
| IS_WITHIN | ALIGNS_RIGHT);
/*
* A overlaps B to the right
*
* |-A-|
* |-B-|
*
* a.start > b.start && a.start < b.end && a.end > b.end
*/
FRAME_t.put("overlapsRight", OVERLAPS_RIGHT);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY | OVERLAPS_LEFT | ALIGNS_LEFT
| STARTS_WITH | MATCHES | IS_WITHIN | IS_AROUND | ENDS_WITH
| ALIGNS_RIGHT | OVERLAPS_RIGHT);
/*
* A succeeds B directly
*
* |-A-|
* |-B-|
*
* a.start == b.end
*/
FRAME_t.put("succeedsDirectly", SUCCEEDS_DIRECTLY);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY | OVERLAPS_LEFT | ALIGNS_LEFT
| STARTS_WITH | MATCHES | IS_WITHIN | IS_AROUND | ENDS_WITH
| ALIGNS_RIGHT | OVERLAPS_RIGHT | SUCCEEDS_DIRECTLY);
/*
* A succeeds B
*
* |-A-|
* |-B-|
*
* a.start > b.end
*/
FRAME_t.put("succeeds", SUCCEEDS);
NEXT_B_t.add(PRECEDES | PRECEDES_DIRECTLY | ALIGNS_LEFT | STARTS_WITH
| MATCHES | IS_WITHIN | ALIGNS_RIGHT | SUCCEEDS_DIRECTLY
| SUCCEEDS);
FRAME = Collections.unmodifiableMap(FRAME_t);
NEXT_B = Collections.unmodifiableList(NEXT_B_t);
};
// Bitvector representing the frame constraint
public int vector;
/**
* Constructs a new Frame Constraint.
*/
public FrameConstraint () {
this.vector = 0;
};
/**
* Add a new valid condition to the Frame Constraint.
*
* @param condition
* A string representing a valid condition.
* See the synopsis for valid condition names.
* @return The {@link FrameConstraint} for chaining.
* @throws QueryException
*/
public FrameConstraint add (String condition) throws QueryException {
int or = FRAME.get(condition);
if (or == 0)
throw new QueryException(706, "Frame type is unknown");
this.vector |= or;
return this;
};
/**
* Add new valid conditions to the Frame Constraint.
*
* @param constraint
* A Frame constraint representing a set
* of valid conditions.
* @return The {@link FrameConstraint} for chaining.
*/
public FrameConstraint add (FrameConstraint constraint) {
this.vector |= constraint.vector;
return this;
};
/**
* Invert the condition set of the frame constraint.
* All valid conditions become invalid, all invalid
* conditions become valid.
*
* @return The {@link FrameConstraint} for chaining.
*/
public FrameConstraint invert () {
this.vector ^= ALL;
return this;
};
/**
* Check if a condition is valid.
*
* @param condition
* A string representing a condition.
* See the synopsis for valid condition names.
* @return A boolean value, indicating if a condition is valid or
* not.
* @throws QueryException
*/
public boolean check (String condition) throws QueryException {
int check = FRAME.get(condition);
if (check == 0)
throw new QueryException(706, "Frame type is unknown");
return this.check(check);
};
/**
* Check if conditions are valid.
*
* @param conditions
* An integer bit vector representing a set of
* conditions.
* @return A boolean value, indicating if at least one condition
* is valid or not.
*/
public boolean check (int conditions) {
return (this.vector & conditions) != 0;
};
/**
* Check if conditions are valid.
*
* @param conditions
* A {@link FrameConstraint} representing a set of
* conditions.
* @return A boolean value, indicating if at least one condition
* is valid or not.
*/
public boolean check (FrameConstraint conditions) {
return (this.vector & conditions.vector) != 0;
};
// Todo: create nextB arraymatrix by adding all combinations of constellation precomputed
// NEXTB[SUCCEEDS_DIRECTLY | SUCCEEDS] = NEXTB[SUCEEDS_DIRECTLY] | NEXTB[SUCCEEDS];
// NextB
// Integer.numberOfTrailingZeros();
private static int _next_b (int constellation) {
return NEXT_B.get(Integer.numberOfTrailingZeros(constellation));
};
// Return a configuration, saying:
// 1. Binary: It matches - it doesn't match!
// 2. Binary: Go on by forwarding a
// 3. Binary: Go on by forwarding b
// 4. The constellation
public int _constellation (Spans a, Spans b) {
// Constellation checks are
// optimized for lazy loading,
// i.e. trying to minimize callings of end()
// A starts after B
if (a.start() > b.start()) {
// if (this.vector & next_b(SUCCEEDS_DIRECTLY) > 0)
// Don't call end() on A
if (a.start() == b.end())
return SUCCEEDS_DIRECTLY;
if (a.start() > b.end())
return SUCCEEDS;
// a) Check if match is possible
// b) Check if mext is possible on A
// Call end() on A
else if (a.end() == b.end()) {
return ALIGNS_RIGHT;
}
else if (a.end() < b.end()) {
return IS_WITHIN;
};
// a.end() > b.end() &&
// a.start() < b.end()
return OVERLAPS_RIGHT;
}
// A starts before B
else if (a.start() < b.start()) {
// Don't call end() on b
if (a.end() == b.start()) {
return PRECEDES_DIRECTLY;
}
else if (a.end() < b.start()) {
return PRECEDES;
}
// Call end() on B
else if (a.end() == b.end()) {
return ENDS_WITH;
}
else if (a.end() > b.end()) {
return IS_AROUND;
};
// a.end() > b.start()
return OVERLAPS_LEFT;
}
// A and B start at the same position
// a.start() == b.start()
else if (a.end() > b.end()) {
return STARTS_WITH;
}
else if (a.end() < b.end()) {
return ALIGNS_LEFT;
};
// a.end() == b.end()
return MATCHES;
};
};