/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* !#
*/
package net.ontopia.topicmaps.query.impl.basic;
import java.util.Collection;
import java.util.Iterator;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.core.index.ClassInstanceIndexIF;
import net.ontopia.topicmaps.query.core.InvalidQueryException;
import net.ontopia.topicmaps.query.impl.utils.Prefetcher;
import net.ontopia.topicmaps.query.impl.utils.PredicateDrivenCostEstimator;
/**
* INTERNAL: Common superclass for the two instance-of predicates.
*/
public abstract class AbstractInstanceOfPredicate implements BasicPredicateIF {
protected TopicMapIF topicmap;
protected ClassInstanceIndexIF index;
public AbstractInstanceOfPredicate(TopicMapIF topicmap) {
this.topicmap = topicmap;
index = (ClassInstanceIndexIF) topicmap.getIndex("net.ontopia.topicmaps.core.index.ClassInstanceIndexIF");
}
public String getSignature() {
return "t t";
}
public int getCost(boolean[] boundparams) {
if (boundparams[0] && boundparams[1])
return PredicateDrivenCostEstimator.FILTER_RESULT;
else if (boundparams[0] && !boundparams[1])
return PredicateDrivenCostEstimator.SMALL_RESULT;
else if (!boundparams[0] && boundparams[1])
return PredicateDrivenCostEstimator.BIG_RESULT;
else
return PredicateDrivenCostEstimator.WHOLE_TM_RESULT;
}
public QueryMatches satisfy(QueryMatches result, Object[] arguments)
throws InvalidQueryException {
start();
int instix = result.getIndex(arguments[0]);
int classix = result.getIndex(arguments[1]);
if (result.data[0][instix] != null) {
if (result.data[0][classix] != null)
// (instance, class)
return verifyPairs(result, instix, classix);
else
// (instance, $CLASS)
return fillInClasses(result, instix, classix);
} else {
if (result.data[0][classix] != null)
// ($INSTANCE, class)
return fillInInstances(result, instix, classix);
else
// ($INSTANCE, $CLASS)
return generateAllPairs(result, instix, classix);
}
}
// --- Data interface
/**
* INTERNAL: Called before the evaluation of a new query, to allow
* resetting of internal caches etc.
*/
protected abstract void start();
/**
* INTERNAL: Should return all applicable types of the given topic.
* In 'instance-of' this includes the supertypes of the topic's types.
*/
protected abstract Collection getClasses(TopicIF instance);
/**
* INTERNAL: Should return all instances of the topic, as seen by the
* specific predicate.
*/
protected abstract Collection getInstances(TopicIF klass);
/**
* INTERNAL: Should return all topic types, as seen by the specific
* predicate.
*/
protected abstract Collection getTypes();
/**
* INTERNAL: Should return all supertypes of the given class,
* including the class itself. In 'direct-instance-of' this should
* just be the class itself.
*/
protected abstract Collection getSupertypes(TopicIF type);
/**
* INTERNAL: Should return all instances of the topic, as seen by the
* specific predicate.
*/
protected Collection getDirectInstances(TopicIF classes) {
return index.getTopics(classes);
}
// --- Implementations of the various cases
private QueryMatches fillInInstances(QueryMatches matches,
int instix, int classix) {
QueryMatches result = new QueryMatches(matches);
for (int ix = 0; ix <= matches.last; ix++) {
if (matches.data[ix][classix] instanceof TopicIF) {
TopicIF klass = (TopicIF) matches.data[ix][classix];
addTo(result, getInstances(klass), instix, matches.data[ix]);
}
}
return result;
}
private QueryMatches fillInClasses(QueryMatches matches,
int instix, int classix) {
QueryMatches result = new QueryMatches(matches);
for (int ix = 0; ix <= matches.last; ix++) {
if (matches.data[ix][instix] instanceof TopicIF) {
TopicIF instance = (TopicIF) matches.data[ix][instix];
addTo(result, getClasses(instance), classix, matches.data[ix]);
}
}
return result;
}
private QueryMatches generateAllPairs(QueryMatches matches,
int instix, int classix) {
QueryMatches result = new QueryMatches(matches);
Iterator it = getTypes().iterator();
while (it.hasNext()) {
TopicIF klass = (TopicIF) it.next();
Object[] instances = getDirectInstances(klass).toArray();
Object[] supers = getSupertypes(klass).toArray();
int instances_length = instances.length;
int supers_length = supers.length;
for (int kix = 0; kix < supers_length; kix++) {
for (int ix = 0; ix < instances_length; ix++) {
for (int i = 0; i <= matches.last; i++) {
if (result.last+1 == result.size)
result.increaseCapacity();
result.last++;
Object[] row = (Object[]) matches.data[i].clone();
row[classix] = supers[kix];
row[instix] = instances[ix];
result.data[result.last] = row;
}
}
}
}
return result;
}
private QueryMatches verifyPairs(QueryMatches matches,
int instix, int classix) {
Prefetcher.prefetch(topicmap, matches, instix,
Prefetcher.TopicIF,
Prefetcher.TopicIF_types, false);
QueryMatches result = new QueryMatches(matches);
for (int ix = 0; ix <= matches.last; ix++) {
if (!(matches.data[ix][instix] instanceof TopicIF))
continue;
TopicIF inst = (TopicIF) matches.data[ix][instix];
Object klass = matches.data[ix][classix];
if (!getClasses(inst).contains(klass))
continue; // not a match, so skip it
if (result.last+1 == result.size)
result.increaseCapacity();
result.last++;
// FIXME: is this really safe? or could row sharing give overwrites?
result.data[result.last] = matches.data[ix];
}
return result;
}
// --- Helpers
/**
* INTERNAL: Given a collection of values, and a column to fill them
* into, and an existing row, that row is duplicated with new values
* for the column for each element of the collection.
*/
private void addTo(QueryMatches result,
Collection theValues, int colix, Object[] row) {
Object[] values = theValues.toArray();
int length = values.length;
for (int ix = 0; ix < length; ix++) {
if (result.last+1 == result.size)
result.increaseCapacity();
result.last++;
Object[] newRow = (Object[]) row.clone();
newRow[colix] = values[ix];
result.data[result.last] = newRow;
}
}
}