package org.xbib.elasticsearch.common.fsa;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
/**
* An FSA with constant-size arc representation produced directly by {@link FSABuilder}.
*
* @see FSABuilder
*/
public final class ConstantArcSizeFSA extends FSA {
/**
* Size of the target address field (constant for the builder).
*/
static final int TARGET_ADDRESS_SIZE = 4;
/**
* Size of the flags field (constant for the builder).
*/
private static final int FLAGS_SIZE = 1;
/**
* Size of the label field (constant for the builder).
*/
private static final int LABEL_SIZE = 1;
/**
* Size of a single arc structure.
*/
static final int ARC_SIZE = FLAGS_SIZE + LABEL_SIZE + TARGET_ADDRESS_SIZE;
/**
* Offset of the flags field inside an arc.
*/
static final int FLAGS_OFFSET = 0;
/**
* Offset of the label field inside an arc.
*/
static final int LABEL_OFFSET = FLAGS_SIZE;
/**
* Offset of the address field inside an arc.
*/
static final int ADDRESS_OFFSET = LABEL_OFFSET + LABEL_SIZE;
/**
* An arc flag indicating the target node of an arc corresponds to a final
* state.
*/
static final int BIT_ARC_FINAL = 1 << 1;
/**
* An arc flag indicating the arc is last within its state.
*/
static final int BIT_ARC_LAST = 1;
/**
* A dummy address of the terminal state.
*/
static final int TERMINAL_STATE = 0;
/**
* An epsilon state. The first and only arc of this state points either
* to the root or to the terminal state, indicating an empty automaton.
*/
private final int epsilon;
/**
* FSA data, serialized as a byte array.
*/
private final byte[] data;
/**
* @param data FSA data. There must be no trailing bytes after the last state.
*/
ConstantArcSizeFSA(byte[] data, int epsilon) {
if (epsilon != 0) {
throw new IllegalArgumentException("Epsilon is not zero?");
}
this.epsilon = epsilon;
this.data = data;
}
@Override
public int getRootNode() {
return getEndNode(getFirstArc(epsilon));
}
@Override
public int getFirstArc(int node) {
return node;
}
@Override
public int getArc(int node, byte label) {
for (int arc = getFirstArc(node); arc != 0; arc = getNextArc(arc)) {
if (getArcLabel(arc) == label) {
return arc;
}
}
return 0;
}
@Override
public int getNextArc(int arc) {
if (isArcLast(arc)) {
return 0;
}
return arc + ARC_SIZE;
}
@Override
public byte getArcLabel(int arc) {
return data[arc + LABEL_OFFSET];
}
/**
* Fills the target state address of an arc.
* @param a arc
*/
private int getArcTarget(int a) {
int arc =a;
arc += ADDRESS_OFFSET;
return (data[arc]) << 24 |
(data[arc + 1] & 0xff) << 16 |
(data[arc + 2] & 0xff) << 8 |
(data[arc + 3] & 0xff);
}
@Override
public boolean isArcFinal(int arc) {
return (data[arc + FLAGS_OFFSET] & BIT_ARC_FINAL) != 0;
}
@Override
public boolean isArcTerminal(int arc) {
return getArcTarget(arc) == 0;
}
private boolean isArcLast(int arc) {
return (data[arc + FLAGS_OFFSET] & BIT_ARC_LAST) != 0;
}
@Override
public int getEndNode(int arc) {
return getArcTarget(arc);
}
@Override
public Set<FSAFlags> getFlags() {
return Collections.emptySet();
}
@Override
public void write(DataOutputStream outputStream) throws IOException {
outputStream.writeInt(data.length);
outputStream.writeInt(epsilon);
outputStream.write(data);
outputStream.close();
}
}