/*
* Copyright (c) 2005- michael lawley and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation
* which accompanies this distribution, and is available by writing to
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Contributors:
* michael lawley
*
*
*
*/
package tefkat.engine;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import tefkat.model.Var;
import tefkat.model.internal.ModelUtils;
/**
* For each tracking class:
* For each (direct & inherited) feature of the class:
* Create a value -> BitSet Map
*
* Create an EObject ArrayList
*
*
* For each instance of the class:
* Add to the ArrayList for the class
* For each feature of the class:
* Get the BitSet for the feature value
* Flip the bit corresponding to the arraylist index
*
* To handle inheritance, do the above for each superclass of the
* instance as well.
*
*
* To query:
* for each supplied non-Var feature value, get the corresponding BitSet
* AND the BitSets
* For each bit set in the result, get the object at index in the ArrayList
*
*
*/
class TrackingManager {
static interface Callback {
public void match(EObject instance, Binding context);
}
Map instancesMap = new HashMap();
Map indexMap = new HashMap();
void query(EClass cls, Object[] query, Callback callback) throws ResolutionException {
List instances = (List) instancesMap.get(cls);
if (null == instances) {
return;
}
// Initially, every instance is a candidate
BitSet candidates = new BitSet();
candidates.set(0, instances.size(), true);
queryCandidates(cls, query, candidates, callback);
}
void query(EClass cls, Object[] query, EObject instance, Callback callback) throws ResolutionException {
List instances = (List) instancesMap.get(cls);
if (null == instances) {
return;
}
// Initially, every instance is a candidate
BitSet candidates = new BitSet();
int index = instances.indexOf(instance);
candidates.set(index, true);
queryCandidates(cls, query, candidates, callback);
}
private void queryCandidates(EClass cls, Object[] query, BitSet candidates, Callback callback) throws ResolutionException {
List instances = (List) instancesMap.get(cls);
if (null == instances) {
return;
}
List matches = new ArrayList();
matches.add(candidates);
// The set of feature, var pairs to get bindings for
List vars = new ArrayList();
for (int i = 0; i < query.length; i++) {
Object[] field = (Object[]) query[i];
EStructuralFeature feature = ModelUtils.getFeature(cls, (String) field[0]);
List values = (List) field[1];
if (values.size() == 1 && values.get(0) instanceof WrappedVar) {
// Vars match everything
vars.add(new Object[] {feature, ((WrappedVar) values.get(0)).getVar()});
} else {
List newMatches = new ArrayList();
for (Iterator itr = values.iterator(); itr.hasNext(); ) {
Object value = itr.next();
BitSetKey key = new BitSetKey(cls, feature, value);
BitSet bs = (BitSet) indexMap.get(key);
for (Iterator mItr = matches.iterator(); mItr.hasNext(); ) {
BitSet mbs = (BitSet) mItr.next();
if (mbs.intersects(bs)) {
BitSet nbs = (BitSet) bs.clone();
nbs.and(mbs);
newMatches.add(nbs);
}
}
}
if (newMatches.size() == 0) {
return;
}
matches = newMatches;
}
}
BitSet result = new BitSet();
for (Iterator itr = matches.iterator(); itr.hasNext(); ) {
BitSet bs = (BitSet) itr.next();
result.or(bs);
}
for (int idx = result.nextSetBit(0); idx >= 0; idx = result.nextSetBit(idx + 1)) {
EObject instance = (EObject) instances.get(idx);
List oldBindings = new ArrayList();
oldBindings.add(new Binding());
for (Iterator fvItr = vars.iterator(); fvItr.hasNext(); ) {
Object[] fv = (Object[]) fvItr.next();
EStructuralFeature feature = (EStructuralFeature) fv[0];
Var var = (Var) fv[1];
Object value = instance.eGet(feature);
List newBindings = null;
if (feature.isMany()) {
newBindings = new ArrayList();
for (Iterator bindingItr = oldBindings.iterator(); bindingItr.hasNext(); ) {
Binding oldUnifier = (Binding) bindingItr.next();
for (Iterator valueItr = ((List) value).iterator(); valueItr.hasNext(); ) {
Binding unifier = new Binding(oldUnifier);
unifier.add(var, valueItr.next());
newBindings.add(unifier);
}
}
ExtentUtil.highlightEdge(instance, value, ExtentUtil.FEATURE_LOOKUP);
} else {
for (Iterator bItr = oldBindings.iterator(); bItr.hasNext(); ) {
Binding binding = (Binding) bItr.next();
binding.add(var, value);
}
}
}
}
return;
}
/**
* Assumes this method has not previously been called
* with the same cls,instance combination and that all the
* features of the instance have been set and will not change.
*
* TODO check that static/dynamic instances won't hurt us
*
* @param cls
* @param instance
*/
void add(EClass cls, EObject instance) {
List instances = (List) instancesMap.get(cls);
if (null == instances) {
instances = new ArrayList();
instancesMap.put(cls, instance);
}
int index = instances.size();
instances.add(instance);
for (Iterator itr = cls.getEAllStructuralFeatures().iterator(); itr.hasNext(); ) {
EStructuralFeature feature = (EStructuralFeature) itr.next();
Object value = instance.eGet(feature);
BitSetKey key = new BitSetKey(cls, feature, value);
BitSet bs = (BitSet) indexMap.get(key);
if (null == bs) {
bs = new BitSet();
indexMap.put(key, bs);
}
bs.set(index, true);
}
for (Iterator itr = cls.getESuperTypes().iterator(); itr.hasNext(); ) {
EClass supercls = (EClass) itr.next();
add(supercls, instance);
}
}
static class BitSetKey {
private final int hashCode;
private final EClass cls;
private final EStructuralFeature feature;
private final Object value;
BitSetKey(EClass cls, EStructuralFeature feature, Object value) {
this.cls = cls;
this.feature = feature;
this.value = value;
hashCode = cls.hashCode() ^ feature.hashCode() ^ value.hashCode();
}
public int hashCode() {
return hashCode;
}
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (other instanceof BitSetKey) {
BitSetKey bs = (BitSetKey) other;
return cls.equals(bs.cls) &&
feature.equals(bs.feature) &&
value.equals(bs.value);
}
return false;
}
}
}