package edu.cmu.minorthird.text; import java.util.*; import edu.cmu.minorthird.util.gui.*; import edu.cmu.minorthird.text.gui.*; import org.apache.log4j.*; /** A TextLabels which is defined by two TextLabels's. * * <p> Operationally, new assertions are passed to the 'outer' TextLabels. * Assertions about property definitions from the outer TextLabels shadow * assertions made in the inner TextLabels, and other assertions are added * to assertions in the inner TextLabels. * * <p> Pragmatically, this means that if you create a NestedTextLabels * from outerLabels and innerLabels, where outerLabels is empty, the * NestedTextLabels will initially look like innerLabels. But if you modify * it, innerLabels will not be changed, so you can at any point easily * revert to the old innerLabels TextLabels. * * * @author William Cohen */ public class NestedTextLabels implements MonotonicTextLabels,Visible{ private static final Logger log=Logger.getLogger(NestedTextLabels.class); private MonotonicTextLabels outer; private TextLabels inner; private Set<String> shadowedProperties=new HashSet<String>(); /** Create a NestedTextLabels. */ public NestedTextLabels(MonotonicTextLabels outer,TextLabels inner){ if(outer.getTextBase()!=inner.getTextBase()) throw new IllegalArgumentException("mismatched text bases?"); this.outer=outer; this.inner=inner; } /** Create a NestedTextLabels with an empty outer labeling. */ public NestedTextLabels(TextLabels inner){ this.outer=new BasicTextLabels(inner.getTextBase()); this.inner=inner; } @Override public TextBase getTextBase(){ return inner.getTextBase(); } @Override public boolean hasDictionary(String dictionary){ return inner.hasDictionary(dictionary)||outer.hasDictionary(dictionary); } @Override public boolean isAnnotatedBy(String s){ return outer.isAnnotatedBy(s)||inner.isAnnotatedBy(s); } @Override public void setAnnotatedBy(String s){ outer.setAnnotatedBy(s); } @Override public void setAnnotatorLoader(AnnotatorLoader newLoader){ outer.setAnnotatorLoader(newLoader); } @Override public AnnotatorLoader getAnnotatorLoader(){ return outer.getAnnotatorLoader(); } @Override public void defineDictionary(String dictName,Set<String> dict){ outer.defineDictionary(dictName,dict); } /** Associate a dictionary from this file */ @Override public void defineDictionary(String dictName,List<String> fileNames, boolean ignoreCase){ outer.defineDictionary(dictName,fileNames,ignoreCase); } /** Return a trie if defined */ @Override public Trie getTrie(){ return outer.getTrie(); } /** Define a trie */ @Override public void defineTrie(List<String> phraseList){ outer.defineTrie(phraseList); } @Override public boolean inDict(Token token,String dictionary){ boolean outDict=outer.hasDictionary(dictionary); boolean innerDict=inner.hasDictionary(dictionary); if(outDict) return outer.inDict(token,dictionary); else if(innerDict) return inner.inDict(token,dictionary); else throw new IllegalArgumentException("undefined dictionary "+dictionary); } /** Effectively, remove the property from this TextLabels. * Specifically ensure that for this property (a) calls to setProperty * do nothing but cause a warning (b) calls to getProperty return null. */ public void shadowProperty(String prop){ shadowedProperties.add(prop); } @Override public void setProperty(Token token,String prop,String value){ if(shadowedProperties.contains(prop)) log.warn("Property "+prop+" has been shadowed"); else outer.setProperty(token,prop,value); } @Override public void setProperty(Token token,String prop,String value,Details details){ if(shadowedProperties.contains(prop)) log.warn("Property "+prop+" has been shadowed"); else outer.setProperty(token,prop,value); } @Override public String getProperty(Token token,String prop){ if(shadowedProperties.contains(prop)) return null; else{ String r=outer.getProperty(token,prop); return r!=null?r:inner.getProperty(token,prop); } } @Override public Iterator<Span> getSpansWithProperty(String prop){ if(shadowedProperties.contains(prop)) return Collections.EMPTY_SET.iterator(); else if(!outer.getSpanProperties().contains(prop)) return inner.getSpansWithProperty(prop); else if(!inner.getSpanProperties().contains(prop)) return outer.getSpansWithProperty(prop); else return new MyUnionIterator(outer.getSpansWithProperty(prop),inner .getSpansWithProperty(prop)); } @Override public Iterator<Span> getSpansWithProperty(String prop,String id){ if(shadowedProperties.contains(prop)) return Collections.EMPTY_SET.iterator(); else if(!outer.getSpanProperties().contains(prop)) return inner.getSpansWithProperty(prop,id); else if(!inner.getSpanProperties().contains(prop)) return outer.getSpansWithProperty(prop,id); else return new MyUnionIterator(outer.getSpansWithProperty(prop,id),inner .getSpansWithProperty(prop,id)); } @Override public Set<String> getTokenProperties(){ Set<String> set=setUnion(outer.getTokenProperties(),inner.getTokenProperties()); set.removeAll(shadowedProperties); return set; } @Override public void setProperty(Span span,String prop,String value){ outer.setProperty(span,prop,value); } @Override public void setProperty(Span span,String prop,String value,Details details){ outer.setProperty(span,prop,value,details); } @Override public String getProperty(Span span,String prop){ String r=outer.getProperty(span,prop); return r!=null?r:inner.getProperty(span,prop); } @Override public Set<String> getSpanProperties(){ return setUnion(outer.getSpanProperties(),inner.getSpanProperties()); } @Override public void addToType(Span span,String type){ if(!inner.hasType(span,type)) outer.addToType(span,type); } @Override public void addToType(Span span,String type,Details details){ if(!inner.hasType(span,type)) outer.addToType(span,type,details); } @Override public boolean hasType(Span span,String type){ return outer.hasType(span,type)||inner.hasType(span,type); } @Override public Iterator<Span> instanceIterator(String type){ if(!outer.isType(type)) return inner.instanceIterator(type); else if(!inner.isType(type)) return outer.instanceIterator(type); else return new MyUnionIterator(outer.instanceIterator(type),inner .instanceIterator(type)); } @Override public Iterator<Span> instanceIterator(String type,String documentId){ if(!outer.isType(type)) return inner.instanceIterator(type,documentId); else if(!inner.isType(type)) return outer.instanceIterator(type,documentId); else return new MyUnionIterator(outer.instanceIterator(type,documentId),inner .instanceIterator(type,documentId)); } @Override public Iterator<Span> closureIterator(String type){ if(!outer.isType(type)) return inner.closureIterator(type); else if(!inner.isType(type)) return outer.closureIterator(type); else return new MyUnionIterator(outer.closureIterator(type),inner .closureIterator(type)); } @Override public Iterator<Span> closureIterator(String type,String documentId){ if(!outer.isType(type)) return inner.closureIterator(type,documentId); else if(!inner.isType(type)) return outer.closureIterator(type,documentId); else return new MyUnionIterator(outer.closureIterator(type,documentId),inner .closureIterator(type,documentId)); } @Override public Set<String> getTypes(){ return setUnion(outer.getTypes(),inner.getTypes()); } @Override public Set<Span> getTypeSet(String type,String documentId){ return setUnion(outer.getTypeSet(type,documentId),inner.getTypeSet(type, documentId)); } @Override public boolean isType(String type){ return outer.isType(type)||inner.isType(type); } @Override public void declareType(String type){ //System.out.println("NestedTextLabels: declareType: "+type); if(!isType(type)) outer.declareType(type); } @Override public Details getDetails(Span span,String type){ Details result=outer.getDetails(span,type); if(result!=null) return result; return inner.getDetails(span,type); } @Override public void require(String annotationType,String fileToLoad){ BasicTextLabels.doRequire(this,annotationType,fileToLoad,outer .getAnnotatorLoader()); } @Override public void require(String annotationType,String fileToLoad, AnnotatorLoader loader){ BasicTextLabels.doRequire(this,annotationType,fileToLoad,loader); } /** Annotate labels with annotator named fileToLoad */ @Override public void annotateWith(String annotationType,String fileToLoad){ BasicTextLabels.annotateWith(this,annotationType,fileToLoad); } @Override public String showTokenProp(TextBase base,String prop){ return "outer: "+outer.showTokenProp(base,prop)+" inner: "+ inner.showTokenProp(base,prop); } // // private routines and classes // private <T> Set<T> setUnion(Set<T> a,Set<T> b){ if(a.isEmpty()) return b; else{ Set<T> u=new HashSet<T>(); u.addAll(a); u.addAll(b); return u; } } private class MyUnionIterator implements Iterator<Span>{ Iterator<Span> i,j,currentLooper; // int estSize=-1; public MyUnionIterator(Iterator<Span> i,Iterator<Span> j){ this.i=i; this.j=j; currentLooper=i; } @Override public void remove(){ currentLooper.remove(); } @Override public boolean hasNext(){ return currentLooper.hasNext()||(currentLooper==i&&j.hasNext()); } @Override public Span next(){ if(currentLooper==i&&!currentLooper.hasNext()) currentLooper=j; return currentLooper.next(); } // public int estimatedSize(){ // return estSize; // } } @Override public Viewer toGUI(){ return new ZoomingTextLabelsViewer(this); } @Override public String toString(){ return "[NestedLabels: outer="+outer+"; inner="+inner+"]"; } }