package main.java.bolts;
import static main.java.utils.constants.WorkberchConstants.INDEX_FIELD;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import main.java.utils.WorkberchTuple;
import main.java.utils.cartesianindex.CartesianIndex;
import main.java.utils.cartesianindex.CartesianLeaf;
import main.java.utils.cartesianindex.CartesianNode;
import main.java.utils.redis.RedisException;
import main.java.utils.redis.RedisHandeler;
import backtype.storm.topology.BasicOutputCollector;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
abstract public class WorkberchOrderBolt extends WorkberchProvenanceBolt {
//FIXME: This flag is temporally, we need to improve the good algorithm
public static boolean REAL_ALGORITHM = false;
private static final long serialVersionUID = 1L;
private final Map<Long, WorkberchTuple> indexMap = new HashMap<Long, WorkberchTuple>();
private final boolean ordered;
private long lastIndex = 0L;
private CartesianIndex createInitialIndexElement(final CartesianIndex indexTemplate) {
if (indexTemplate.isLeafValue()) {
return new CartesianLeaf(0L);
} else {
final List<CartesianIndex> initListNodes = new ArrayList<CartesianIndex>();
for (final CartesianIndex initNodeTemplate : indexTemplate.getNodes()) {
initListNodes.add(createInitialIndexElement(initNodeTemplate));
}
return new CartesianNode(initListNodes);
}
}
private CartesianIndex createNextElementOnIndex(final CartesianIndex previewsIndex, final CartesianIndex templateIndex) {
if (templateIndex.isLeafValue()) {
final Long templateValue = templateIndex.getValue();
if (templateValue > 0 && templateValue < previewsIndex.getValue()) {
return new CartesianLeaf(0L);
} else {
return new CartesianLeaf(previewsIndex.getValue() + 1);
}
} else {
final List<CartesianIndex> previewsNodes = Lists.reverse(previewsIndex.getNodes());
final List<CartesianIndex> templateNodes = Lists.reverse(templateIndex.getNodes());
final Iterator<CartesianIndex> iterPreviews = previewsNodes.iterator();
final Iterator<CartesianIndex> iterTemplate = templateNodes.iterator();
CartesianIndex nextSubPreviews;
CartesianIndex nextSubTemplate;
CartesianIndex nextSubValue;
CartesianIndex resetTuple;
final List<CartesianIndex> nextNewNodes = new ArrayList<CartesianIndex>();
do {
nextSubPreviews = iterPreviews.next();
nextSubTemplate = iterTemplate.next();
nextSubValue = createNextElementOnIndex(nextSubPreviews, nextSubTemplate);
nextNewNodes.add(nextSubValue);
resetTuple = createInitialIndexElement(nextSubTemplate);
} while (resetTuple.equals(nextSubValue) && iterPreviews.hasNext());
if (iterPreviews.hasNext()) {
for (final Iterator<CartesianIndex> iterator = iterPreviews; iterator.hasNext();) {
nextNewNodes.add(iterator.next());
}
}
return new CartesianNode(Lists.reverse(nextNewNodes));
}
}
private void makePlainIndex(final CartesianIndex templateIndex, final Map<CartesianIndex, WorkberchTuple> cartesianIndex) {
if (REAL_ALGORITHM) {
makePlainIndexOnMapRecurtion(templateIndex, cartesianIndex, new CartesianNode(new ArrayList<CartesianIndex>()));
}
else {
makePlainIndexOnLinealIndex(templateIndex.getNodes(), cartesianIndex, new CartesianNode(new ArrayList<CartesianIndex>()));
}
}
private boolean makePlainIndexOnLinealIndex(final List<CartesianIndex> nodes, final Map<CartesianIndex, WorkberchTuple> cartesianIndex, final CartesianIndex currentKey) {
final boolean isLastDimention = nodes.isEmpty();
if (isLastDimention) {
final boolean existValue;
if(existValue = cartesianIndex.containsKey(currentKey)){
indexMap.put(lastIndex, cartesianIndex.get(currentKey));
lastIndex++;
return existValue;
}
return existValue;
} else {
long indexValue = 0L;
boolean existValues = false;
boolean existSomeIndex = false;
do {
final CartesianIndex currentIndexLeaf = new CartesianLeaf(indexValue);
currentKey.getNodes().add(currentIndexLeaf);
existValues = makePlainIndexOnLinealIndex(nodes.subList(1, nodes.size()), cartesianIndex, currentKey);
currentKey.getNodes().remove(currentIndexLeaf);
indexValue++;
existSomeIndex = existSomeIndex || existValues;
} while (existValues);
return existSomeIndex;
}
}
private boolean makePlainIndexOnMapRecurtion(final CartesianIndex templateIndex, final Map<CartesianIndex, WorkberchTuple> cartesianIndex,
final CartesianIndex currentKey) {
final List<CartesianIndex> listOfNodes = templateIndex.getNodes();
final CartesianIndex templateIndexHead = listOfNodes.iterator().next();
final CartesianIndex templateIndexTail = new CartesianNode(listOfNodes.subList(1, listOfNodes.size()));
final boolean isLastDimention = templateIndexTail.getNodes().isEmpty();
CartesianIndex currentIndex = createInitialIndexElement(templateIndexHead);
currentKey.getNodes().add(currentIndex);
boolean someValueWork = false;
boolean areMoreValuesAvailabe = true;
while (areMoreValuesAvailabe) {
final boolean tupleExists = isLastDimention ? cartesianIndex.containsKey(currentKey) : makePlainIndexOnMapRecurtion(templateIndexTail,
cartesianIndex, currentKey);
if (!tupleExists) {
areMoreValuesAvailabe = updateTemplateWithLastTops(templateIndexHead, currentIndex);
} else if (isLastDimention && tupleExists) {
indexMap.put(lastIndex, cartesianIndex.get(currentKey));
someValueWork = true;
lastIndex++;
}
currentKey.getNodes().remove(currentKey.getNodes().size() - 1);
currentIndex = createNextElementOnIndex(currentIndex, templateIndexHead);
currentKey.getNodes().add(currentIndex);
}
currentKey.getNodes().remove(currentKey.getNodes().size() - 1);
return someValueWork;
}
private boolean updateTemplateWithLastTops(final CartesianIndex templateIndexHead, final CartesianIndex currentKey) {
if (templateIndexHead.isLeafValue()) {
if (templateIndexHead.getValue() < 0L) {
templateIndexHead.setValue(currentKey.getValue() - 1);
return true;
}
return false;
} else {
final List<CartesianIndex> templateNodes = Lists.reverse(templateIndexHead.getNodes());
final List<CartesianIndex> currentNodes = Lists.reverse(currentKey.getNodes());
final Iterator<CartesianIndex> iterTemplate = templateNodes.iterator();
final Iterator<CartesianIndex> iterCurrent = currentNodes.iterator();
boolean areMoreValuesAvailabe;
do {
areMoreValuesAvailabe = updateTemplateWithLastTops(iterTemplate.next(), iterCurrent.next());
} while (iterTemplate.hasNext() && !areMoreValuesAvailabe);
return areMoreValuesAvailabe;
}
}
private CartesianIndex cleanTemplateIndex(final CartesianIndex templateTemplate) {
if (templateTemplate.isLeafValue()) {
return new CartesianLeaf(-1L);
} else {
final List<CartesianIndex> initListNodes = new ArrayList<CartesianIndex>();
for (final CartesianIndex initNodeTemplate : templateTemplate.getNodes()) {
initListNodes.add(cleanTemplateIndex(initNodeTemplate));
}
return new CartesianNode(initListNodes);
}
}
private void emitAllSavedTuplesInOrder(final CartesianIndex templateIndex, final BasicOutputCollector collector, final String uuid) {
try {
final Map<CartesianIndex, WorkberchTuple> cartesianIndex = RedisHandeler.loadCartesianIndexObjects(getBoltId());
if (!templateIndex.isLeafValue()) {
makePlainIndex(cleanTemplateIndex(templateIndex), cartesianIndex);
}
WorkberchTuple tuple;
lastIndex = 0L;
do {
tuple = indexMap.get(lastIndex);
tuple.setPlainIndex(lastIndex);
executeOrdered(tuple, collector, !indexMap.containsKey(++lastIndex), uuid);
} while (indexMap.containsKey(lastIndex));
} catch (final RedisException e) {
Throwables.propagate(e);
}
}
private void processReceivedTupleCommingInOrder(final WorkberchTuple input, final BasicOutputCollector collector, final boolean lastValues,
final String uuid) {
final Long currentLong = (Long) input.getValues().get(INDEX_FIELD);
final long currentIndex = currentLong.longValue();
if (currentIndex > lastIndex) {
indexMap.put(currentLong, input);
} else if (currentIndex == lastIndex) {
indexMap.put(currentLong, input);
WorkberchTuple tuple;
do {
tuple = indexMap.get(lastIndex);
executeOrdered(tuple, collector, !indexMap.containsKey(++lastIndex) && lastValues, uuid);
} while (indexMap.containsKey(lastIndex));
}
}
private void proccessReceivedTupleCommingWithoutOrder(final WorkberchTuple input) {
try {
RedisHandeler.saveCartesianIndexObject(getBoltId(), input);
} catch (final RedisException e) {
Throwables.propagate(e);
}
}
private void processReceivedTuple(final WorkberchTuple input, final BasicOutputCollector collector, final boolean lastValues, final String uuid) {
if (ordered) {
processReceivedTupleCommingInOrder(input, collector, lastValues, uuid);
} else {
proccessReceivedTupleCommingWithoutOrder(input);
}
};
public WorkberchOrderBolt(final String guid, final List<String> outputFields, final Boolean ordered) {
super(guid, outputFields);
this.ordered = ordered;
}
@Override
public void executeLogic(final WorkberchTuple input, final BasicOutputCollector collector, final boolean lastValues, final String uuid) {
processReceivedTuple(input, collector, lastValues, uuid);
if (lastValues && !ordered) {
emitAllSavedTuplesInOrder((CartesianIndex) input.getValues().get(INDEX_FIELD), collector, uuid);
}
}
abstract public void executeOrdered(final WorkberchTuple input, final BasicOutputCollector collector, final boolean lastValues, final String uuid);
}