package org.hypergraphdb.query;
import org.hypergraphdb.HGHandle;
import org.hypergraphdb.HGPersistentHandle;
import org.hypergraphdb.HGQuery.hg;
import org.hypergraphdb.HyperGraph;
import org.hypergraphdb.HGLink;
import org.hypergraphdb.util.Ref;
import org.hypergraphdb.util.TempLink;
/**
* <p>
* A <code>PositionedLinkCondition</code> constraints the query result set to
* links pointing to a target atom positioned within a predetermined range in
* the link tuple. The range is specified with a lower and an upper bound where
* negative values mean that position are evaluated from the last target in the link.
* For example a value of -1 means the last target in the link tuple, -2 means the
* one before the last etc. In addition, the condition allows you to specify that
* the target should be position anywhere else except in the specified range. You can
* do this by passing <code>true</code> as the <code>complement</code> parameter
* in the condition's constructor.
* </p>
*
* <p>
* <b>Note</b> that the lower and upper bound in the range specification are inclusing.
* A range of [1, 5] means all positions 1 to 5, included, are examined. Position counting
* starts at 0 and, as mentioned above, position -1 means the last element.
* </p>
*
* @author Alain Picard, Borislav Iordanov
*/
public class PositionedIncidentCondition implements HGQueryCondition, HGAtomPredicate
{
private Ref<HGHandle> targetRef = null;
private Ref<Integer> lowerBoundRef, upperBoundRef;
private Ref<Boolean> complementRef;
public PositionedIncidentCondition()
{
}
public PositionedIncidentCondition(Ref<HGHandle> target, Ref<Integer> position)
{
this(target, position, hg.constant(false));
}
public PositionedIncidentCondition(Ref<HGHandle> target,
Ref<Integer> position,
Ref<Boolean> complement)
{
this.targetRef = target;
this.lowerBoundRef = position;
this.upperBoundRef = position;
this.complementRef = complement;
}
public PositionedIncidentCondition(Ref<HGHandle> target,
Ref<Integer> lowerBound,
Ref<Integer> upperBound,
Ref<Boolean> complement)
{
this.targetRef = target;
this.lowerBoundRef = lowerBound;
this.upperBoundRef = upperBound;
this.complementRef = complement;
}
public Ref<HGHandle> getTargetRef()
{
return targetRef;
}
public void setTargetRef(Ref<HGHandle> targetRef)
{
this.targetRef = targetRef;
}
public Ref<Integer> getLowerBoundRef()
{
return lowerBoundRef;
}
public void setLowerBoundRef(Ref<Integer> lowerBoundRef)
{
this.lowerBoundRef = lowerBoundRef;
}
public Ref<Integer> getUpperBoundRef()
{
return upperBoundRef;
}
public void setUpperBoundRef(Ref<Integer> upperBoundRef)
{
this.upperBoundRef = upperBoundRef;
}
public Ref<Boolean> getComplementRef()
{
return complementRef;
}
public void setComplementRef(Ref<Boolean> complementRef)
{
this.complementRef = complementRef;
}
public boolean satisfies(HyperGraph hg, HGHandle handle)
{
HGPersistentHandle target = targetRef.get().getPersistent();
HGLink link = null;
// If the atom corresponding to 'handle' is already in the cache, there
// is no point
// fetching it from permanent storage. Otherwise, there's no point
// caching the actual atom...
if (hg.isLoaded(handle))
{
Object atom = hg.get(handle);
if (!(atom instanceof HGLink))
return false;
link = (HGLink) atom;
}
else
{
HGPersistentHandle[] A = hg.getStore().getLink(handle.getPersistent());
if (A == null || A.length <=2) //this indicates that we don't have a link
return false;
link = new TempLink(A, 2);
}
int ub = upperBoundRef.get();
if (ub < 0)
ub = link.getArity() + ub;
int lb = lowerBoundRef.get();
if (lb < 0)
lb = link.getArity() + lb;
assert lb <= ub;
if (complementRef.get())
{
for (int i = 0; i < lb; i++)
if (link.getTargetAt(i).equals(target))
return true;
for (int i = ub + 1; i < link.getArity(); i++)
if (link.getTargetAt(i).equals(target))
return true;
return false;
}
else
{
for (int i = lb; i <= ub; i++)
if (link.getTargetAt(i).equals(target))
return true;
return false;
}
}
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result
+ ((complementRef == null) ? 0 : complementRef.hashCode());
result = prime * result
+ ((lowerBoundRef == null) ? 0 : lowerBoundRef.hashCode());
result = prime * result
+ ((targetRef == null) ? 0 : targetRef.hashCode());
result = prime * result
+ ((upperBoundRef == null) ? 0 : upperBoundRef.hashCode());
return result;
}
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PositionedIncidentCondition other = (PositionedIncidentCondition) obj;
if (complementRef == null)
{
if (other.complementRef != null)
return false;
}
else if (!complementRef.equals(other.complementRef))
return false;
if (lowerBoundRef == null)
{
if (other.lowerBoundRef != null)
return false;
}
else if (!lowerBoundRef.equals(other.lowerBoundRef))
return false;
if (targetRef == null)
{
if (other.targetRef != null)
return false;
}
else if (!targetRef.equals(other.targetRef))
return false;
if (upperBoundRef == null)
{
if (other.upperBoundRef != null)
return false;
}
else if (!upperBoundRef.equals(other.upperBoundRef))
return false;
return true;
}
public String toString()
{
StringBuffer result = new StringBuffer("incidentAt(");
result.append(targetRef.get().toString());
result.append(',');
result.append(lowerBoundRef.get().toString());
result.append(',');
result.append(upperBoundRef.get().toString());
result.append(',');
result.append(complementRef.get().toString());
result.append(")");
return result.toString();
}
}