package ecologylab.bigsemantics.model.text; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Observable; import java.util.Observer; import java.util.Set; import ecologylab.generic.IFeatureVector; /** * A CompositeTermVector acts like a TermVector on the outside, but on the inside * it maintains a reference to each TermVector added to it. This means that if the content * of a TermVector is modified, this modification bubbles up through the CompositeTermVector * which is observing all it's component term vectors. * @author jmole * */ public class CompositeTermVector extends Observable implements Observer, ITermVector { private TermVector compositeTermVector = new TermVector(); private HashMap<ITermVector, Double> termVectors = new HashMap<ITermVector, Double>(); public CompositeTermVector () { } /** * Adds a Term Vector to this Composite Term Vectors collection, multiplying it by a scalar. * * @param tv * The Term Vector you wish to add. * @param multiplier * The scalar multiple. */ @Override public void add ( double multiplier, ITermVector tv ) { HashMap<ITermVector, Double> v; v = termVectors; synchronized (v) { double oldMultiplier = 0; if (v.containsKey(tv)) oldMultiplier = v.get(tv); else tv.addObserver(this); v.put(tv, oldMultiplier + multiplier); compositeTermVector.add(multiplier, tv); setChanged(); notifyObservers(); } } /** * Adds a Term Vector to this Composite Term Vector's collection * * @param tv * The term vector you wish to add. */ @Override public void add ( ITermVector tv ) { if (tv != null) add(1, tv); } /** * Removes a Term Vector from this Composite Term Vectors collection. * * @param tv * The Term Vector you wish to remove. */ public void remove ( ITermVector tv ) { //FIXME this is a bandaid for a race condition elsewhere, in which still in use term vectors // are getting recycled. where is this happening? why? if (termVectors == null) return; Double removal = null; synchronized (termVectors) { removal = termVectors.remove(tv); } if (removal != null) { tv.deleteObserver(this); compositeTermVector.add(-removal, tv); setChanged(); notifyObservers(); } } private boolean notifiedAlready; @Override public void update ( Observable o, Object arg ) { Set set = (Set) arg; if (set == null || set.contains(this)) return; set.add(this); notifiedAlready = false; rebuildCompositeTermVector(); setChanged(); if(notifiedAlready == false) { notifiedAlready = true; notifyObservers(set); } } @Override public void notifyObservers() { notifyObservers(new HashSet()); } @Override public synchronized void recycle ( ) { if (!hasObservers()) { if (termVectors != null) { for (ITermVector tv : termVectors.keySet()) { tv.deleteObserver(this); if (!tv.hasObservers()) tv.recycle(); } termVectors.clear(); termVectors = null; } if (compositeTermVector != null) { compositeTermVector.recycle(); compositeTermVector = null; } } } @Override public boolean isRecycled() { return compositeTermVector == null; } private void rebuildCompositeTermVector ( ) { TermVector c = new TermVector(compositeTermVector.size()); synchronized (termVectors) { for (IFeatureVector<Term> t : termVectors.keySet()) c.add(termVectors.get(t), t); } compositeTermVector = c; } public void reinitialize() { compositeTermVector = new TermVector(); termVectors = new HashMap<ITermVector, Double>(); } @Override public double dot ( IFeatureVector<Term> v ) { return compositeTermVector.dot(v); } @Override public Set<Term> elements ( ) { return compositeTermVector.elements(); } @Override public double get ( Term term ) { return compositeTermVector.get(term); } @Override public Map<Term, Double> map ( ) { return compositeTermVector != null ? compositeTermVector.map() : null; } @Override public Set<Double> values ( ) { return compositeTermVector.values(); } public Set<ITermVector> componentVectors ( ) { return termVectors.keySet(); } @Override public String toString ( ) { if (termVectors == null) return ""; StringBuilder s = new StringBuilder("["); synchronized (termVectors) { for (IFeatureVector<Term> v : termVectors.keySet()) { s.append(v.toString()); s.append(", "); } } s.append("]"); return s.toString(); } @Override public double norm ( ) { return compositeTermVector.norm(); } @Override public double max ( ) { return compositeTermVector.max(); } @Override public double idfDot ( IFeatureVector<Term> v ) { return (compositeTermVector != null) ? compositeTermVector.idfDot(v) : Double.MIN_VALUE; } @Override public TermVector unit ( ) { return compositeTermVector.unit(); } @Override public int commonDimensions ( IFeatureVector<Term> v ) { return compositeTermVector.commonDimensions(v); } @Override public double dotSimplex ( IFeatureVector<Term> v ) { return compositeTermVector.dotSimplex(v); } @Override public TermVector simplex ( ) { return compositeTermVector.simplex(); } @Override public double idfDotSimplex ( IFeatureVector<Term> v ) { return compositeTermVector.idfDotSimplex(v); } public int size() { return compositeTermVector.size(); } @Override public boolean hasObservers() { return countObservers() > 0; } }