001package org.biojava.bio.annodb; 002 003import java.beans.XMLDecoder; 004import java.beans.XMLEncoder; 005import java.io.BufferedInputStream; 006import java.io.BufferedOutputStream; 007import java.io.BufferedReader; 008import java.io.File; 009import java.io.FileInputStream; 010import java.io.FileOutputStream; 011import java.io.FileWriter; 012import java.io.IOException; 013import java.io.PrintWriter; 014import java.io.Serializable; 015import java.lang.reflect.Method; 016import java.lang.reflect.Modifier; 017import java.util.Iterator; 018import java.util.List; 019 020import org.biojava.bio.Annotation; 021import org.biojava.bio.AnnotationTools; 022import org.biojava.bio.AnnotationType; 023import org.biojava.bio.BioException; 024import org.biojava.bio.program.indexdb.BioStore; 025import org.biojava.bio.program.indexdb.BioStoreFactory; 026import org.biojava.bio.program.indexdb.Record; 027import org.biojava.bio.program.tagvalue.AnnotationBuilder; 028import org.biojava.bio.program.tagvalue.Index2Model; 029import org.biojava.bio.program.tagvalue.Indexer2; 030import org.biojava.bio.program.tagvalue.Parser; 031import org.biojava.bio.program.tagvalue.ParserListener; 032import org.biojava.bio.program.tagvalue.TagValueListener; 033import org.biojava.bio.seq.io.filterxml.XMLAnnotationTypeHandler; 034import org.biojava.bio.seq.io.filterxml.XMLAnnotationTypeWriter; 035import org.biojava.utils.AssertionFailure; 036import org.biojava.utils.CommitFailure; 037import org.biojava.utils.ParserException; 038import org.biojava.utils.io.RandomAccessReader; 039import org.biojava.utils.stax.SAX2StAXAdaptor; 040import org.biojava.utils.xml.PrettyXMLWriter; 041import org.biojava.utils.xml.XMLWriter; 042import org.xml.sax.SAXException; 043import org.xml.sax.XMLReader; 044import org.xml.sax.helpers.XMLReaderFactory; 045 046/** 047 * <p>A database of Annotation instances backed by an indexed file set.</p> 048 * 049 * @author Matthew Pocock 050 * @since 1.3 051 */ 052public class IndexedAnnotationDB 053implements AnnotationDB { 054 private final BioStore store; 055 private final AnnotationType schema; 056 private final ParserListenerFactory plFactory; 057 private final ParserListener parserListener; 058 private final AnnotationBuilder annBuilder; 059 private final Parser recordParser; 060 061 /** 062 * Create a new IndexedAnnotationDB. 063 * 064 * @param dbName 065 * @param storeLoc 066 * @param model 067 * @param toIndex 068 * @param maxKeyLen 069 * @param schema 070 * @param plFactory 071 * @throws BioException 072 * @throws CommitFailure 073 * @throws IOException 074 * @throws ParserException 075 */ 076 public IndexedAnnotationDB( 077 String dbName, 078 File storeLoc, 079 Index2Model model, 080 List toIndex, 081 int maxKeyLen, 082 AnnotationType schema, 083 ParserListenerFactory plFactory 084 ) throws BioException, CommitFailure, IOException, ParserException { 085 // state 086 BioStoreFactory bsf = new BioStoreFactory(); 087 bsf.setStoreName(dbName); 088 bsf.setPrimaryKey(model.getPrimaryKeyName()); 089 bsf.setStoreLocation(storeLoc); 090 091 for(Iterator i = model.getKeys().iterator(); i.hasNext(); ) { 092 String key = (String) i.next(); 093 bsf.addKey(key, maxKeyLen); 094 } 095 096 this.store = bsf.createBioStore(); 097 this.schema = schema; 098 this.plFactory = plFactory; 099 this.annBuilder = new AnnotationBuilder(schema); 100 this.parserListener = plFactory.getParserListener(annBuilder); 101 this.recordParser = new Parser(); 102 103 // persistance 104 File factoryFile = new File(store.getLocation(), "ParserListenerFactory.xml"); 105 XMLEncoder xmlEnc = new XMLEncoder( 106 new BufferedOutputStream( 107 new FileOutputStream( 108 factoryFile 109 ) 110 ) 111 ); 112 xmlEnc.writeObject(plFactory); 113 xmlEnc.close(); 114 115 File schemaFile = new File(store.getLocation(), "schema.xml"); 116 PrintWriter schemaPW = new PrintWriter( 117 new FileWriter( 118 schemaFile 119 ) 120 ); 121 XMLWriter schemaWriter = new PrettyXMLWriter(schemaPW); 122 XMLAnnotationTypeWriter schemaTW = new XMLAnnotationTypeWriter(); 123 schemaTW.writeAnnotationType(schema, schemaWriter); 124 schemaPW.flush(); 125 schemaPW.close(); 126 127 for(Iterator fi = toIndex.iterator(); fi.hasNext(); ) { 128 File file = (File) fi.next(); 129 130 Indexer2 ndx = new Indexer2(file, store, model); 131 ParserListener pl = plFactory.getParserListener(ndx); 132 Parser parser = new Parser(); 133 while(parser.read(ndx.getReader(), pl.getParser(), pl.getListener())) { 134 ; 135 } 136 } 137 138 store.commit(); 139 } 140 141 /** 142 * Initialise the db from a store. 143 * 144 * @param store the BioStore to initalise from 145 * @throws IOException if there was an IO fault accessing the store 146 * @throws SAXException if the XML configuration file is corrupted 147 */ 148 public IndexedAnnotationDB(BioStore store) throws IOException, SAXException { 149 this.store = store; 150 151 File factoryFile = new File(store.getLocation(), "ParserListenerFactory.xml"); 152 XMLDecoder xmlDec = new XMLDecoder( 153 new BufferedInputStream( 154 new FileInputStream( 155 factoryFile 156 ) 157 ) 158 ); 159 this.plFactory = (ParserListenerFactory) xmlDec.readObject(); 160 xmlDec.close(); 161 162 XMLReader parser = XMLReaderFactory.createXMLReader(); 163 XMLAnnotationTypeHandler annTypeH = new XMLAnnotationTypeHandler(); 164 parser.setContentHandler( 165 new SAX2StAXAdaptor( 166 annTypeH 167 ) 168 ); 169 this.schema = annTypeH.getAnnotationType(); 170 171 this.annBuilder = new AnnotationBuilder(schema); 172 this.parserListener = plFactory.getParserListener(annBuilder); 173 this.recordParser = new Parser(); 174 } 175 176 public String getName() { 177 return store.getName(); 178 } 179 180 public AnnotationType getSchema() { 181 return schema; 182 } 183 184 public Iterator iterator() { 185 return new Iterator() { 186 Iterator rli = store.getRecordList().iterator(); 187 188 public boolean hasNext() { 189 return rli.hasNext(); 190 } 191 192 public Object next() { 193 try { 194 return process((Record) rli.next()); 195 } catch (Exception e) { 196 throw new RuntimeException(e); 197 } 198 } 199 200 public void remove() { 201 throw new UnsupportedOperationException(); 202 } 203 }; 204 } 205 206 public int size() { 207 return store.getRecordList().size(); 208 } 209 210 public AnnotationDB filter(AnnotationType at) { 211 AnnotationType schema = AnnotationTools.intersection(at, this.schema); 212 213 if(schema != AnnotationType.NONE) { 214 return new LazyFilteredAnnotationDB("", this, schema); 215 } else { 216 return AnnotationDB.EMPTY; 217 } 218 } 219 220 public AnnotationDB search(AnnotationType at) { 221 return new LazySearchedAnnotationDB("", this, at); 222 } 223 224 /** 225 * Get the ParserListenerFactory used by this IndexedAnnotationDB. 226 * 227 * @return the ParserListenerFactory 228 */ 229 public ParserListenerFactory getParserListenerFactory() { 230 return plFactory; 231 } 232 233 private Annotation process(Record rec) 234 throws IOException, ParserException { 235 RandomAccessReader rar = new RandomAccessReader(rec.getFile()); 236 rar.seek(rec.getOffset()); 237 BufferedReader reader = new BufferedReader(rar); 238 recordParser.read(reader, parserListener.getParser(), parserListener.getListener()); 239 return annBuilder.getLast(); 240 } 241 242 /** 243 * A factory for retrieving parsers and listeners. 244 * 245 * @author Matthew Pocock 246 * @since 1.3 247 */ 248 public static interface ParserListenerFactory 249 extends Serializable { 250 /** 251 * Get the ParserListener for a TagValueListener. 252 * 253 * @param listener the TagValueListener to process 254 * @return the ParserListener for this 255 */ 256 public ParserListener getParserListener(TagValueListener listener); 257 } 258 259 /** 260 * An implementation of ParserListenerFactory that uses a static method. 261 * 262 * @author Matthew Pocock 263 * @since 1.3 264 */ 265 public static class StaticMethodRPFactory 266 implements ParserListenerFactory { 267 private final Method method; 268 269 /** 270 * Create a new StaticMethodRPFactory for a method. 271 * 272 * @param method a Method to use 273 * @throws IllegalArgumentException if the Method is not statically scoped, 274 * or does not return a ParserListener or take a single argument of type 275 * TagValueListener 276 */ 277 public StaticMethodRPFactory(Method method) 278 throws IllegalArgumentException { 279 if( (method.getModifiers() & Modifier.STATIC) != Modifier.STATIC ) { 280 throw new IllegalArgumentException("Method must be static"); 281 } 282 283 if(method.getReturnType() != ParserListener.class) { 284 throw new IllegalArgumentException("Method must return a ParserListener instance"); 285 } 286 287 if( 288 method.getParameterTypes().length != 1 || 289 method.getParameterTypes()[0] != TagValueListener.class 290 ) { 291 throw new IllegalArgumentException("Method must accept a single TagValueListener as it's sole parameter"); 292 } 293 294 this.method = method; 295 } 296 297 /** 298 * Get the Method used. 299 * 300 * @return the Method used. 301 */ 302 public Method getMethod() { 303 return method; 304 } 305 306 public ParserListener getParserListener(TagValueListener tvl) { 307 try { 308 return (ParserListener) method.invoke(null, new Object[] { tvl }); 309 } catch (Exception e) { 310 throw new AssertionFailure("Could not invoke underlying method.", e); 311 } 312 } 313 } 314} 315