CorpusImpl.java
001 /*
002  *  CorpusImpl.java
003  *
004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Hamish Cunningham, 11/Feb/2000
013  *
014  *  $Id: CorpusImpl.java 17604 2014-03-09 10:08:13Z markagreenwood $
015  */
016 
017 package gate.corpora;
018 
019 import gate.Corpus;
020 import gate.Document;
021 import gate.Factory;
022 import gate.FeatureMap;
023 import gate.Gate;
024 import gate.Resource;
025 import gate.creole.AbstractLanguageResource;
026 import gate.creole.CustomDuplication;
027 import gate.creole.ResourceInstantiationException;
028 import gate.creole.metadata.CreoleParameter;
029 import gate.creole.metadata.CreoleResource;
030 import gate.creole.metadata.Optional;
031 import gate.event.CorpusEvent;
032 import gate.event.CorpusListener;
033 import gate.event.CreoleEvent;
034 import gate.event.CreoleListener;
035 import gate.event.StatusListener;
036 import gate.util.BomStrippingInputStreamReader;
037 import gate.util.Err;
038 import gate.util.Files;
039 import gate.util.Strings;
040 
041 import java.io.BufferedReader;
042 import java.io.File;
043 import java.io.FileFilter;
044 import java.io.FileNotFoundException;
045 import java.io.IOException;
046 import java.io.Serializable;
047 import java.net.URL;
048 import java.util.AbstractList;
049 import java.util.ArrayList;
050 import java.util.Arrays;
051 import java.util.Collection;
052 import java.util.Collections;
053 import java.util.Comparator;
054 import java.util.Iterator;
055 import java.util.List;
056 import java.util.ListIterator;
057 import java.util.Vector;
058 
059 /**
060  * Corpora are sets of Document. They are ordered by lexicographic
061  * collation on Url.
062  */
063 @CreoleResource(name = "GATE Corpus", comment = "GATE transient corpus.", interfaceName = "gate.Corpus", icon = "corpus-trans", helpURL = "http://gate.ac.uk/userguide/sec:developer:loadlr")
064 public class CorpusImpl extends AbstractLanguageResource implements Corpus,
065                                                         CreoleListener,
066                                                         CustomDuplication {
067 
068   public CorpusImpl() {
069     supportList = Collections.synchronizedList(new VerboseList());
070     Gate.getCreoleRegister().addCreoleListener(this);
071   }
072 
073   /**
074    * Gets the names of the documents in this corpus.
075    
076    @return {@link List} of Strings representing the names of the
077    *         documents in this corpus.
078    */
079   @Override
080   public List<String> getDocumentNames() {
081     ArrayList<String> res = new ArrayList<String>(supportList.size());
082     for(Object document : supportList) {
083       res.add(((Document)document).getName());
084     }
085     return res;
086   }
087 
088   /**
089    * Gets the name of a document in this corpus.
090    
091    @param index the index of the document
092    @return a String value representing the name of the document at
093    *         <tt>index</tt> in this corpus.
094    */
095   @Override
096   public String getDocumentName(int index) {
097     return supportList.get(index).getName();
098   }
099 
100   /**
101    * This method does not make sense for transient corpora, so it does
102    * nothing.
103    */
104   @Override
105   public void unloadDocument(Document doc) {
106     return;
107   }
108 
109   /**
110    * The underlying list that holds the documents in this corpus.
111    */
112   protected List<Document> supportList = null;
113 
114   /**
115    * A proxy list that stores the actual data in an internal list and
116    * forwards all operations to that one but it also fires the
117    * appropriate corpus events when necessary. It also does some type
118    * checking so only Documents are accepted as corpus members.
119    */
120   protected class VerboseList extends AbstractList<Document> implements Serializable {
121 
122     private static final long serialVersionUID = 3483062654980468826L;
123 
124     VerboseList() {
125       data = new ArrayList<Document>();
126     }
127 
128     @Override
129     public Document get(int index) {
130       return data.get(index);
131     }
132 
133     @Override
134     public int size() {
135       return data.size();
136     }
137 
138     @Override
139     public Document set(int index, Document element) {
140         Document oldDoc = data.set(index, element);
141 
142         // fire the 2 events
143         fireDocumentRemoved(new CorpusEvent(CorpusImpl.this, oldDoc, index,
144                 CorpusEvent.DOCUMENT_REMOVED));
145         fireDocumentAdded(new CorpusEvent(CorpusImpl.this, element, index,
146                 CorpusEvent.DOCUMENT_ADDED));
147         return oldDoc;
148     }
149 
150     @Override
151     public void add(int index, Document element) {
152         data.add(index, element);
153 
154         // fire the event
155         fireDocumentAdded(new CorpusEvent(CorpusImpl.this, element,
156                 index, CorpusEvent.DOCUMENT_ADDED));
157     }
158 
159     @Override
160     public Document remove(int index) {
161       Document oldDoc = data.remove(index);
162 
163       fireDocumentRemoved(new CorpusEvent(CorpusImpl.this, oldDoc, index,
164               CorpusEvent.DOCUMENT_REMOVED));
165       return oldDoc;
166     }
167 
168     /**
169      * The List containing the actual data.
170      */
171     List<Document> data;
172   }
173 
174   /**
175    * This method returns true when the document is already loaded in
176    * memory
177    */
178   @Override
179   public boolean isDocumentLoaded(int index) {
180     return true;
181   }
182 
183   protected void clearDocList() {
184     if(supportList == nullreturn;
185     supportList.clear();
186   }
187 
188   // List methods
189   // java docs will be automatically copied from the List interface.
190 
191   @Override
192   public int size() {
193     return supportList.size();
194   }
195 
196   @Override
197   public boolean isEmpty() {
198     return supportList.isEmpty();
199   }
200 
201   @Override
202   public boolean contains(Object o) {
203     return supportList.contains(o);
204   }
205 
206   @Override
207   public Iterator<Document> iterator() {
208     return supportList.iterator();
209   }
210 
211   @Override
212   public Object[] toArray() {
213     return supportList.toArray();
214   }
215 
216   @Override
217   public <T> T[] toArray(T[] a) {
218     return supportList.toArray(a);
219   }
220 
221   @Override
222   public boolean add(Document o) {
223     return supportList.add(o);
224   }
225 
226   @Override
227   public boolean remove(Object o) {
228     return supportList.remove(o);
229   }
230 
231   @Override
232   public boolean containsAll(Collection<?> c) {
233     return supportList.containsAll(c);
234   }
235 
236   @Override
237   public boolean addAll(Collection<? extends Document> c) {
238     return supportList.addAll(c);
239   }
240 
241   @Override
242   public boolean addAll(int index, Collection<? extends Document> c) {
243     return supportList.addAll(index, c);
244   }
245 
246   @Override
247   public boolean removeAll(Collection<?> c) {
248     return supportList.removeAll(c);
249   }
250 
251   @Override
252   public boolean retainAll(Collection<?> c) {
253     return supportList.retainAll(c);
254   }
255 
256   @Override
257   public void clear() {
258     supportList.clear();
259   }
260 
261   @Override
262   public boolean equals(Object o) {
263     if(!(instanceof CorpusImpl)) return false;
264 
265     return supportList.equals(o);
266   }
267 
268   @Override
269   public int hashCode() {
270     return supportList.hashCode();
271   }
272 
273   @Override
274   public Document get(int index) {
275     return supportList.get(index);
276   }
277 
278   @Override
279   public Document set(int index, Document element) {
280     return supportList.set(index, element);
281   }
282 
283   @Override
284   public void add(int index, Document element) {
285     supportList.add(index, element);
286   }
287 
288   @Override
289   public Document remove(int index) {
290     return supportList.remove(index);
291   }
292 
293   @Override
294   public int indexOf(Object o) {
295     return supportList.indexOf(o);
296   }
297 
298   @Override
299   public int lastIndexOf(Object o) {
300     return supportList.lastIndexOf(o);
301   }
302 
303   @Override
304   public ListIterator<Document> listIterator() {
305     return supportList.listIterator();
306   }
307 
308   @Override
309   public ListIterator<Document> listIterator(int index) {
310     return supportList.listIterator(index);
311   }
312 
313   @Override
314   public List<Document> subList(int fromIndex, int toIndex) {
315     return supportList.subList(fromIndex, toIndex);
316   }
317 
318   /** Construction */
319 
320   @Override
321   public void cleanup() {
322     Gate.getCreoleRegister().removeCreoleListener(this);
323   }
324 
325   /** Initialise this resource, and return it. */
326   @Override
327   public Resource init() {
328     if(documentsList != null && !documentsList.isEmpty()) {
329       addAll(documentsList);
330     }
331     return this;
332   // init()
333 
334   /**
335    * Fills the provided corpus with documents created on the fly from
336    * selected files in a directory. Uses a {@link FileFilter} to select
337    * which files will be used and which will be ignored. A simple file
338    * filter based on extensions is provided in the Gate distribution (
339    {@link gate.util.ExtensionFileFilter}).
340    
341    @param corpus the corpus to be populated
342    @param directory the directory from which the files will be picked.
343    *          This parameter is an URL for uniformity. It needs to be a
344    *          URL of type file otherwise an InvalidArgumentException
345    *          will be thrown.
346    @param filter the file filter used to select files from the target
347    *          directory. If the filter is <tt>null</tt> all the files
348    *          will be accepted.
349    @param encoding the encoding to be used for reading the documents
350    @param recurseDirectories should the directory be parsed
351    *          recursively?. If <tt>true</tt> all the files from the
352    *          provided directory and all its children directories (on as
353    *          many levels as necessary) will be picked if accepted by
354    *          the filter otherwise the children directories will be
355    *          ignored.
356    @throws java.io.IOException if a file doesn't exist
357    */
358   public static void populate(Corpus corpus, URL directory, FileFilter filter,
359           String encoding, boolean recurseDirectoriesthrows IOException {
360     populate(corpus, directory, filter, encoding, null, recurseDirectories);
361   }
362 
363   /**
364    * Fills the provided corpus with documents created on the fly from
365    * selected files in a directory. Uses a {@link FileFilter} to select
366    * which files will be used and which will be ignored. A simple file
367    * filter based on extensions is provided in the Gate distribution (
368    {@link gate.util.ExtensionFileFilter}).
369    
370    @param corpus the corpus to be populated
371    @param directory the directory from which the files will be picked.
372    *          This parameter is an URL for uniformity. It needs to be a
373    *          URL of type file otherwise an InvalidArgumentException
374    *          will be thrown.
375    @param filter the file filter used to select files from the target
376    *          directory. If the filter is <tt>null</tt> all the files
377    *          will be accepted.
378    @param encoding the encoding to be used for reading the documents
379    @param recurseDirectories should the directory be parsed
380    *          recursively?. If <tt>true</tt> all the files from the
381    *          provided directory and all its children directories (on as
382    *          many levels as necessary) will be picked if accepted by
383    *          the filter otherwise the children directories will be
384    *          ignored.
385    @throws java.io.IOException if a file doesn't exist
386    */
387   public static void populate(Corpus corpus, URL directory, FileFilter filter,
388           String encoding, String mimeType, boolean recurseDirectories)
389           throws IOException {
390 
391     // check input
392     if(!directory.getProtocol().equalsIgnoreCase("file"))
393       throw new IllegalArgumentException(
394               "The URL provided is not of type \"file:\"!");
395 
396     File dir = Files.fileFromURL(directory);
397     if(!dir.exists()) throw new FileNotFoundException(dir.toString());
398 
399     if(!dir.isDirectory())
400       throw new IllegalArgumentException(dir.getAbsolutePath()
401               " is not a directory!");
402 
403     File[] files;
404     // populate the corpus
405     if(recurseDirectories) {
406       files = Files.listFilesRecursively(dir, filter);
407     }
408     else {
409       files = dir.listFiles(filter);
410     }
411 
412     if(files == null) {
413       return;
414     }
415 
416     // sort the files alphabetically regardless of their paths
417     Arrays.sort(files, new Comparator<File>() {
418       @Override
419       public int compare(File f1, File f2) {
420         return f1.getName().compareTo(f2.getName());
421       }
422     });
423 
424     // create the GATE documents
425     for(File file : files) {
426       if(file.isDirectory()) {
427         continue;
428       }
429       StatusListener sListener = (StatusListener)Gate.getListeners().get(
430               "gate.event.StatusListener");
431       if(sListener != null)
432         sListener.statusChanged("Reading: " + file.getName());
433       String docName = file.getName() "_" + Gate.genSym();
434       FeatureMap params = Factory.newFeatureMap();
435       params.put(Document.DOCUMENT_URL_PARAMETER_NAME, file.toURI().toURL());
436       if(encoding != null)
437         params.put(Document.DOCUMENT_ENCODING_PARAMETER_NAME, encoding);
438       if(mimeType != null)
439         params.put(Document.DOCUMENT_MIME_TYPE_PARAMETER_NAME, mimeType);
440 
441       try {
442         Document doc = (Document)Factory.createResource(DocumentImpl.class
443                 .getName(), params, null, docName);
444         corpus.add(doc);
445         if(corpus.getLRPersistenceId() != null) {
446           // persistent corpus -> unload the document
447           corpus.unloadDocument(doc);
448           Factory.deleteResource(doc);
449         }
450       }
451       catch(Throwable t) {
452         String nl = Strings.getNl();
453         Err.prln("WARNING: Corpus.populate could not instantiate document" + nl
454                 "  Document name was: " + docName + nl + "  Exception was: "
455                 + t + nl + nl);
456         t.printStackTrace();
457       }
458       if(sListener != nullsListener.statusChanged(file.getName() " read");
459     }
460 
461   }// public static void populate
462 
463   /**
464    * Fills this corpus with documents created from files in a directory.
465    
466    @param filter the file filter used to select files from the target
467    *          directory. If the filter is <tt>null</tt> all the files
468    *          will be accepted.
469    @param directory the directory from which the files will be picked.
470    *          This parameter is an URL for uniformity. It needs to be a
471    *          URL of type file otherwise an InvalidArgumentException
472    *          will be thrown. An implementation for this method is
473    *          provided as a static method at
474    *          {@link gate.corpora.CorpusImpl#populate(Corpus, URL, FileFilter, String, boolean)}
475    *          .
476    @param encoding the encoding to be used for reading the documents
477    @param recurseDirectories should the directory be parsed
478    *          recursively?. If <tt>true</tt> all the files from the
479    *          provided directory and all its children directories (on as
480    *          many levels as necessary) will be picked if accepted by
481    *          the filter otherwise the children directories will be
482    *          ignored.
483    */
484   @Override
485   public void populate(URL directory, FileFilter filter, String encoding,
486           boolean recurseDirectoriesthrows IOException,
487           ResourceInstantiationException {
488     populate(this, directory, filter, encoding, null, recurseDirectories);
489   }
490 
491   /**
492    * Fills this corpus with documents created from files in a directory.
493    
494    @param filter the file filter used to select files from the target
495    *          directory. If the filter is <tt>null</tt> all the files
496    *          will be accepted.
497    @param directory the directory from which the files will be picked.
498    *          This parameter is an URL for uniformity. It needs to be a
499    *          URL of type file otherwise an InvalidArgumentException
500    *          will be thrown. An implementation for this method is
501    *          provided as a static method at
502    *          {@link gate.corpora.CorpusImpl#populate(Corpus, URL, FileFilter, String, boolean)}
503    *          .
504    @param encoding the encoding to be used for reading the documents
505    *@param mimeType the mime type to be used when loading documents. If
506    *          null, then the mime type will be detected automatically.
507    
508    @param recurseDirectories should the directory be parsed
509    *          recursively?. If <tt>true</tt> all the files from the
510    *          provided directory and all its children directories (on as
511    *          many levels as necessary) will be picked if accepted by
512    *          the filter otherwise the children directories will be
513    *          ignored.
514    */
515   @Override
516   public void populate(URL directory, FileFilter filter, String encoding,
517           String mimeType, boolean recurseDirectoriesthrows IOException,
518           ResourceInstantiationException {
519     populate(this, directory, filter, encoding, mimeType, recurseDirectories);
520   }
521 
522   /**
523    * Fills the provided corpus with documents extracted from the
524    * provided trec file.
525    
526    @param corpus the corpus to be populated.
527    @param singleConcatenatedFile the trec file.
528    @param documentRootElement text between this element (start and
529    *          end) is considered for creating a new document.
530    @param encoding the encoding of the trec file.
531    @param numberOfDocumentsToExtract extracts the specified number of
532    *          documents from the trecweb file; -1 to indicate all files.
533    @param mimeType the mime type which determines how the document is handled
534    @return total length of populated documents in the corpus in number
535    *         of bytes
536    @throws java.io.IOException
537    */  
538   public static long populate(Corpus corpus, URL singleConcatenatedFile,
539       String documentRootElement, String encoding,
540       int numberOfDocumentsToExtract, String documentNamePrefix,
541       String mimeType, boolean includeRootElementthrows IOException 
542     
543     StatusListener sListener = (StatusListener)gate.Gate.getListeners().get("gate.event.StatusListener");
544     
545     // obtain the root element that user has provided
546     // content between the start and end of root element is considered
547     // for creating documents
548     documentRootElement = documentRootElement.toLowerCase();
549 
550     // document name prefix could be an empty string
551     documentNamePrefix = documentNamePrefix == null "" : documentNamePrefix
552             .trim()
553             "_";
554 
555     // we start a new document when we find <documentRootElement> and
556     // close it when we find </documentRootElement>
557     BufferedReader br = null;
558     try {
559       
560       if(encoding != null && encoding.trim().length() != 0) {
561         br = new BomStrippingInputStreamReader(singleConcatenatedFile.openStream(),
562                 encoding, 10485760);
563       }
564       else {
565         br = new BomStrippingInputStreamReader(singleConcatenatedFile.openStream(),
566                 10485760);
567       }
568 
569       // reading line by line
570       String line = br.readLine();
571 
572       // this is where we store document content
573       StringBuilder documentString = new StringBuilder();
574 
575       // toggle switch to indicate search for start element
576       boolean searchingForStartElement = true;
577 
578       // keeping count of number of documents extracted
579       int count = 1;
580 
581       // length in bytes read so far (to return)
582       long lengthInBytes = 0;
583 
584       // continue until reached the end of file
585       while(line != null) {
586 
587         // lowercase the line in order to match documentRootElement in any case
588         String lowerCasedLine = line.toLowerCase();
589 
590         // if searching for startElement?
591         if(searchingForStartElement) {
592 
593           // may be its with attributes
594           int index = lowerCasedLine.indexOf("<" + documentRootElement + " ");
595 
596           // may be no attributes?
597           if(index == -1) {
598             index = lowerCasedLine.indexOf("<" + documentRootElement + ">");
599           }
600 
601           // if index <0, we are out of the content boundaries, so simply
602           // skip the current line and start reading from the next line
603           if(index != -1) {
604             // if found, that's the first line
605             line = line.substring(index);
606             searchingForStartElement = false;
607           }
608           else {
609             line = br.readLine();
610           }
611         }
612         else {
613 
614           // now searching for last element
615           int index = lowerCasedLine.indexOf("</" + documentRootElement + ">");
616 
617           // if not found.. this is the content of a new document
618           if(index == -1) {
619             documentString.append(line + "\n");
620             line = br.readLine();
621           }
622           else {
623 
624             // found.. then end the document
625             documentString.append(line.substring(0, index + documentRootElement.length() 3));
626 
627             // getting ready for the next document
628             searchingForStartElement = true;
629 
630             // here lets create a new document create the doc
631             if(sListener != nullsListener.statusChanged("Creating Document Number :" + count);
632             
633             String docName = documentNamePrefix + count + "_" + Gate.genSym();
634             
635             String docContent = documentString.toString();
636             
637             if (!includeRootElement)
638               docContent = docContent.substring(docContent.indexOf(">")+1, docContent.lastIndexOf("<"));
639             
640             FeatureMap params = Factory.newFeatureMap();
641             if (mimeType != nullparams.put(Document.DOCUMENT_MIME_TYPE_PARAMETER_NAME, mimeType);            
642             params.put(Document.DOCUMENT_STRING_CONTENT_PARAMETER_NAME, docContent);
643             if(encoding != null && encoding.trim().length() 0)
644               params.put(Document.DOCUMENT_ENCODING_PARAMETER_NAME, encoding)
645             
646             // calculate the length
647             lengthInBytes += docContent.getBytes().length;            
648 
649             try {
650               Document doc = (Document)Factory.createResource(DocumentImpl.class.getName(), params, null, docName);
651               count++;
652               corpus.add(doc);
653               
654               if(corpus.getLRPersistenceId() != null) {
655                 // persistent corpus -> unload the document
656                 corpus.unloadDocument(doc);
657                 Factory.deleteResource(doc);
658               }
659               
660               // already extracted requested num of documents?
661               if((count - 1== numberOfDocumentsToExtractbreak;
662             }
663             catch(Throwable t) {
664               String nl = Strings.getNl();
665               Err.prln("WARNING: Corpus.populate could not instantiate document" + nl
666                   "  Document name was: " + docName + nl
667                   "  Exception was: " + t + nl + nl);
668               t.printStackTrace();
669             }
670             
671             documentString = new StringBuilder();
672             if(sListener != nullsListener.statusChanged(docName + " created!");
673            
674             line = line.substring(index + documentRootElement.length() 3);
675             if (line.trim().equals("")) line = br.readLine();
676           }
677         }
678       }
679       return lengthInBytes;
680     }
681     finally {
682       if(br != nullbr.close();
683     }
684   }// public static void populate
685 
686   /**
687    * Fills the provided corpus with documents extracted from the
688    * provided single concatenated file.
689    
690    @param singleConcatenatedFile the single concatenated file to load.
691    @param documentRootElement content between the start and end of
692    *          this element is considered for documents.
693    @param encoding the encoding of the trec file.
694    @param numberOfFilesToExtract indicates the number of files to
695    *          extract from the trecweb file.
696    @param documentNamePrefix the prefix to use for document names when
697    *          creating from
698    @param mimeType the mime type which determines how the document is handled
699    @return total length of populated documents in the corpus in number
700    *         of bytes
701    */ 
702   @Override
703   public long populate(URL singleConcatenatedFile, String documentRootElement,
704       String encoding, int numberOfFilesToExtract,
705       String documentNamePrefix, String mimeType, boolean includeRootElementthrows IOException,
706       ResourceInstantiationException {
707     return CorpusImpl.populate(this, singleConcatenatedFile,
708         documentRootElement, encoding, numberOfFilesToExtract,
709         documentNamePrefix, mimeType, includeRootElement);
710 }
711 
712   @Override
713   public synchronized void removeCorpusListener(CorpusListener l) {
714     if(corpusListeners != null && corpusListeners.contains(l)) {
715       @SuppressWarnings("unchecked")
716       Vector<CorpusListener> v = (Vector<CorpusListener>)corpusListeners.clone();
717       v.removeElement(l);
718       corpusListeners = v;
719     }
720   }
721 
722   @Override
723   public synchronized void addCorpusListener(CorpusListener l) {
724     @SuppressWarnings("unchecked")
725     Vector<CorpusListener> v = corpusListeners == null
726             new Vector<CorpusListener>(2)
727             (Vector<CorpusListener>)corpusListeners.clone();
728     if(!v.contains(l)) {
729       v.addElement(l);
730       corpusListeners = v;
731     }
732   }
733 
734   /**
735    * Custom duplication for a corpus - duplicate this corpus in the
736    * usual way, then duplicate the documents in this corpus and add them
737    * to the duplicate.
738    */
739   @Override
740   public Resource duplicate(Factory.DuplicationContext ctx)
741           throws ResourceInstantiationException {
742     Corpus newCorpus = (Corpus)Factory.defaultDuplicate(this, ctx);
743     for(Document d : this) {
744       newCorpus.add((Document)Factory.duplicate(d, ctx));
745     }
746     return newCorpus;
747   }
748 
749   /** Freeze the serialization UID. */
750   static final long serialVersionUID = -1113142759053898456L;
751 
752   private transient Vector<CorpusListener> corpusListeners;
753 
754   protected transient List<Document> documentsList;
755 
756   protected void fireDocumentAdded(CorpusEvent e) {
757     if(corpusListeners != null) {
758       Vector<CorpusListener> listeners = corpusListeners;
759       int count = listeners.size();
760       for(int i = 0; i < count; i++) {
761         listeners.elementAt(i).documentAdded(e);
762       }
763     }
764   }
765 
766   protected void fireDocumentRemoved(CorpusEvent e) {
767     if(corpusListeners != null) {
768       Vector<CorpusListener> listeners = corpusListeners;
769       int count = listeners.size();
770       for(int i = 0; i < count; i++) {
771         listeners.elementAt(i).documentRemoved(e);
772       }
773     }
774   }
775 
776   @Optional
777   @CreoleParameter(collectionElementType = Document.class, comment = "A list of GATE documents")
778   public void setDocumentsList(java.util.List<Document> documentsList) {
779     this.documentsList = documentsList;
780   }
781 
782   public java.util.List<Document> getDocumentsList() {
783     return documentsList;
784   }
785 
786   @Override
787   public void resourceLoaded(CreoleEvent e) {
788   }
789 
790   @Override
791   public void resourceUnloaded(CreoleEvent e) {
792     Resource res = e.getResource();
793     // remove all occurences
794     if(res instanceof Documentwhile(contains(res))
795       remove(res);
796   }
797 
798   @Override
799   public void resourceRenamed(Resource resource, String oldName, String newName) {
800   }
801 
802   @Override
803   public void datastoreOpened(CreoleEvent e) {
804   }
805 
806   @Override
807   public void datastoreCreated(CreoleEvent e) {
808   }
809 
810   @Override
811   public void datastoreClosed(CreoleEvent e) {
812   }
813 // class CorpusImpl