/* * This file is part of the HyperGraphDB source distribution. This is copyrighted * software. For permitted uses, licensing options and redistribution, please see * the LicensingInformation file at the root level of the distribution. * * Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved. */ package org.hypergraphdb.query.impl; import java.util.NoSuchElementException; import org.hypergraphdb.HGRandomAccessResult; import org.hypergraphdb.HGSearchResult; /** * <p> * The <code>ZigZagIntersectionResult</code> operates on two sorted, random access * result sets. * </p> * * @author Borislav Iordanov */ @SuppressWarnings("unchecked") public class ZigZagIntersectionResult<T> implements HGRandomAccessResult<T> // , RSCombiner<T> { private static final Object UNKNOWN = new Object(); private HGRandomAccessResult<T> left, right; private Object current = UNKNOWN, next = UNKNOWN, prev = UNKNOWN; private void swap() { HGRandomAccessResult<T> tmp = left; left = right; right = tmp; } private T advance() { boolean use_next = true; while (true) { if (!left.hasNext() && use_next || !right.hasNext()) return null; T x; if (use_next) { x = left.next(); } else { x = left.current(); use_next = true; } // System.out.println("at " + x); switch (right.goTo(x, false)) { case found: { return x; } case close: { use_next = false; swap(); break; } default: { // this means that all we have reached EOF since 'x', the // element on the left is greater than all elements on the right return null; } } } } private T back() { T result = null; while (true) { if (!left.hasPrev() || !right.hasPrev()) break; T x = left.prev(); if (right.goTo(x, false) == GotoResult.found) { result = x; break; } else swap(); } return result; } // Here, we try to make the current position of this zig-zag result // set be the current position of the left_or_right parameter or the closest // position following that current. If we succeed, we return // true, otherwise we return false. private boolean positionTo(HGRandomAccessResult<T> left_or_right) { // Make sure the argument 'left_or_right' is the left rs in the loop below if (left != left_or_right) swap(); while (true) { switch (right.goTo(left.current(), false)) { case found: { current = left.current(); next = prev = UNKNOWN; return true; } case close: { swap(); break; } default: { return false; } } } } public final static class Combiner<T> implements RSCombiner<T> { public HGSearchResult<T> combine(HGSearchResult<T> left, HGSearchResult<T> right) { return new ZigZagIntersectionResult<T>((HGRandomAccessResult<T>)left, (HGRandomAccessResult<T>)right); } } // public ZigZagIntersectionResult() // { // } public ZigZagIntersectionResult(HGRandomAccessResult<T> left, HGRandomAccessResult<T> right) { this.left = left; this.right = right; } // public void init(HGSearchResult<T> left, HGSearchResult<T> right) // { // this.left = (HGRandomAccessResult<T>)left; // this.right = (HGRandomAccessResult<T>)right; // } // public void reset() { // current = UNKNOWN; // next = UNKNOWN; // prev = UNKNOWN; // } public void goBeforeFirst() { left.goBeforeFirst(); right.goBeforeFirst(); } public void goAfterLast() { left.goAfterLast(); right.goAfterLast(); } public GotoResult goTo(T value, boolean exactMatch) { // We need to save state of both left and right cursor. Because of swapping, save the current // left and the current right in local variables. HGRandomAccessResult<T> starting_left = left, starting_right = right; T save_left = null; T save_right = null; try { save_left = left.current(); save_right = right.current(); } catch (NoSuchElementException ex) { // it means that one of them is empty since we always advance them both in 'init' return GotoResult.nothing; } GotoResult r_l = left.goTo(value, exactMatch); if (r_l == GotoResult.nothing) return GotoResult.nothing; GotoResult r_r = right.goTo(value, exactMatch); if (r_r == GotoResult.nothing) { starting_left.goTo(save_left, true); // restore current position of left... return GotoResult.nothing; } if (r_l == GotoResult.found) { if (r_r == GotoResult.found) { current = left.current(); next = prev = UNKNOWN; return GotoResult.found; } else // r_r == close { if (positionTo(right)) return GotoResult.close; else { starting_left.goTo(save_left, true); starting_right.goTo(save_right, true); return GotoResult.nothing; } } } else // r_l == close { if (r_r == GotoResult.found) { if (positionTo(left)) return GotoResult.close; else { starting_left.goTo(save_left, true); starting_right.goTo(save_right, true); return GotoResult.nothing; } } else // both are 'close' { int cmp = ((Comparable<T>)left.current()).compareTo(right.current()); if (cmp == 0 && positionTo(left) || cmp > 0 && positionTo(left) || positionTo(right)) return GotoResult.close; else { starting_left.goTo(save_left, true); starting_right.goTo(save_right, true); return GotoResult.nothing; } } } } public void close() { left.close(); right.close(); } public T current() { if (current == UNKNOWN) throw new NoSuchElementException(); else return (T)current; } public boolean isOrdered() { return true; } public boolean hasPrev() { if (prev == UNKNOWN) prev = back(); return prev != null; } public T prev() { if (!hasPrev()) throw new NoSuchElementException(); else { next = current; current = prev; prev = UNKNOWN; return (T)current; } } public boolean hasNext() { if (next == UNKNOWN) next = advance(); return next != null; } public T next() { if (!hasNext()) throw new NoSuchElementException(); else { prev = current; current = next; next = UNKNOWN; return (T)current; } } public void remove() { throw new UnsupportedOperationException(); } }