SerialDataStore.java
001 /*
002  *  SerialDataStore.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, 19/Jan/2001
013  *
014  *  $Id: SerialDataStore.java 17662 2014-03-14 16:19:05Z markagreenwood $
015  */
016 
017 package gate.persist;
018 
019 import gate.Corpus;
020 import gate.DataStore;
021 import gate.Document;
022 import gate.Factory;
023 import gate.FeatureMap;
024 import gate.Gate;
025 import gate.LanguageResource;
026 import gate.corpora.SerialCorpusImpl;
027 import gate.creole.ResourceData;
028 import gate.event.DatastoreEvent;
029 import gate.event.DatastoreListener;
030 import gate.util.AbstractFeatureBearer;
031 import gate.util.Files;
032 import gate.util.GateRuntimeException;
033 import gate.util.Out;
034 import gate.util.Strings;
035 
036 import java.io.BufferedInputStream;
037 import java.io.BufferedOutputStream;
038 import java.io.BufferedReader;
039 import java.io.File;
040 import java.io.FileInputStream;
041 import java.io.FileOutputStream;
042 import java.io.FileReader;
043 import java.io.IOException;
044 import java.io.InputStream;
045 import java.io.ObjectInputStream;
046 import java.io.ObjectOutputStream;
047 import java.io.OutputStream;
048 import java.io.OutputStreamWriter;
049 import java.net.MalformedURLException;
050 import java.net.URISyntaxException;
051 import java.net.URL;
052 import java.util.ArrayList;
053 import java.util.Arrays;
054 import java.util.Date;
055 import java.util.List;
056 import java.util.Random;
057 import java.util.Vector;
058 import java.util.zip.GZIPInputStream;
059 import java.util.zip.GZIPOutputStream;
060 
061 /**
062  * A data store based on Java serialisation.
063  */
064 public class SerialDataStore
065 extends AbstractFeatureBearer implements DataStore {
066 
067   private static final long serialVersionUID = -2109852254191554517L;
068 
069   /** Debug flag */
070   private static final boolean DEBUG = false;
071 
072   /** The name of the datastore */
073   protected String name;
074 
075   /**
076    * Construction requires a file protocol URL
077    * pointing to the storage directory used for
078    * the serialised classes. <B>NOTE:</B> should not be called except by
079    * GATE code.
080    */
081   public SerialDataStore(String storageDirUrlthrows PersistenceException {
082     setStorageUrl(storageDirUrl);
083   // construction from URL
084 
085   /**
086    * Default construction. <B>NOTE:</B> should not be called except by
087    * GATE code.
088    */
089   public SerialDataStore() { };
090 
091   /**
092    * The directory used for the serialised classes.
093    */
094   protected File storageDir;
095 
096   /** Set method for storage URL */
097   public void setStorageDir(File storageDir) { this.storageDir = storageDir; }
098 
099   /** Get method for storage URL */
100   public File getStorageDir() { return storageDir; }
101 
102   /** Set the URL for the underlying storage mechanism. */
103   @Override
104   public void setStorageUrl(String urlStringthrows PersistenceException {
105     URL storageUrl = null;
106     try {
107      storageUrl  = new URL(urlString);
108     catch (java.net.MalformedURLException ex) {
109       throw new PersistenceException(
110         "The URL passed is not correct: " + urlString
111       );
112     }
113     if(! storageUrl.getProtocol().equalsIgnoreCase("file"))
114       throw new PersistenceException(
115         "A serial data store needs a file URL, not " + storageUrl
116       );
117     try {
118       this.storageDir = new File(storageUrl.toURI());
119     catch(URISyntaxException use){
120       this.storageDir = Files.fileFromURL(storageUrl);
121     }
122   // setStorageUrl
123 
124   /** Get the URL for the underlying storage mechanism. */
125   @Override
126   public String getStorageUrl() {
127     if(storageDir == nullreturn null;
128 
129     URL u = null;
130     try u = storageDir.toURI().toURL()catch(MalformedURLException e) {
131       // we can assume that this never happens as storageUrl should always
132       // be a valid file and therefore convertable to URL
133     }
134 
135     return u.toString();
136   // getStorageUrl()
137 
138   /** Create a new data store. This tries to create a directory in
139     * the local file system. If the directory already exists and is
140     * non-empty, or is
141     * a file, or cannot be created, PersistenceException is thrown.
142     */
143   @Override
144   public void create()
145   throws PersistenceException {
146     if(storageDir == null)
147       throw new PersistenceException("null storage directory: cannot create");
148 
149     if(! storageDir.exists()) { // if doesn't exist create it
150       
151       if(! storageDir.mkdir())
152         throw new
153           PersistenceException("cannot create directory " + storageDir);
154     else // must be empty
155       String[] existingFiles = filterIgnoredFileNames(storageDir.list());
156       if((existingFiles == null || existingFiles.length == 0) )
157         throw new PersistenceException(
158           "directory "+ storageDir +" is not empty: cannot use for data store"
159         );
160     }
161 
162     // dump the version file
163     try {
164       File versionFile = getVersionFile();
165       OutputStreamWriter osw = new OutputStreamWriter(
166         new FileOutputStream(versionFile)
167       );
168       osw.write(versionNumber + Strings.getNl());
169       osw.close();
170     catch(IOException e) {
171       throw new PersistenceException("couldn't write version file: " + e);
172     }
173   // create()
174 
175   /** The name of the version file */
176   private static String versionFileName = "__GATE_SerialDataStore__";
177 
178   /** The protocol version of the currently open data store */
179   protected String currentProtocolVersion = null;
180 
181   /** Get a File for the protocol version file. */
182   protected File getVersionFile() throws IOException {
183     return new File(storageDir, versionFileName);
184   // getVersionFile
185 
186   /**
187    * Version number for variations in the storage protocol.
188    * Protocol versions:
189    <UL>
190    <LI>
191    * 1.0: uncompressed. Originally had no version file - to read a 1.0
192    * SerialDataStore that has no version file add a version file containing
193    * the line "1.0".
194    <LI>
195    * 1.1: has a version file. Uses GZIP compression.
196    </UL>
197    * This variable stores the version of the current level of the
198    * protocol, NOT the level in use in the currently open data store.
199    */
200   protected static final String versionNumber = "1.1";
201 
202   /** List of valid protocol version numbers. */
203   protected static final String[] protocolVersionNumbers = {
204     "1.0",
205     "1.1"
206   }// protocolVersionNumbers
207 
208   /** Check a version number for validity. */
209   protected static boolean isValidProtocolVersion(String versionNumber) {
210     if(versionNumber == null)
211       return false;
212 
213     for(int i = 0; i < protocolVersionNumbers.length; i++)
214       if(protocolVersionNumbers[i].equals(versionNumber))
215         return true;
216 
217     return false;
218   // isValidProtocolVersion
219 
220   /** Delete the data store.
221     */
222   @Override
223   public void delete() throws PersistenceException {
224     if(storageDir == null || ! Files.rmdir(storageDir))
225       throw new PersistenceException("couldn't delete " + storageDir);
226 
227     Gate.getDataStoreRegister().remove(this);
228   // delete()
229 
230   /** Delete a resource from the data store.
231     */
232   @Override
233   public void delete(String lrClassName, Object lrPersistenceId)
234   throws PersistenceException {
235 
236     // find the subdirectory for resources of this type
237     File resourceTypeDirectory = new File(storageDir, lrClassName);
238     if(
239       (! resourceTypeDirectory.exists()) ||
240       (! resourceTypeDirectory.isDirectory())
241     ) {
242       throw new PersistenceException("Can't find " + resourceTypeDirectory);
243     }
244 
245     // create a File to representing the resource storage file
246     File resourceFile = new File(resourceTypeDirectory, (String)lrPersistenceId);
247     if(! resourceFile.exists() || ! resourceFile.isFile())
248       throw new PersistenceException("Can't find file " + resourceFile);
249 
250     // delete the beast
251     if(! resourceFile.delete())
252       throw new PersistenceException("Can't delete file " + resourceFile);
253 
254     // if there are no more resources of this type, delete the dir too
255     if(filterIgnoredFileNames(resourceTypeDirectory.list()).length == 0)
256       if(! resourceTypeDirectory.delete())
257         throw new PersistenceException("Can't delete " + resourceTypeDirectory);
258 
259     //let the world know about it
260     fireResourceDeleted(
261       new DatastoreEvent(
262         this, DatastoreEvent.RESOURCE_DELETED, null, lrPersistenceId
263       )
264     );
265   // delete(lr)
266   
267   /** Adopt a resource for persistence. */  
268   @Override
269   public LanguageResource adopt(LanguageResource lr)
270   throws PersistenceException {
271 
272     //ignore security info
273 
274     // check the LR's current DS
275     DataStore currentDS = lr.getDataStore();
276     if(currentDS == null) {  // an orphan - do the adoption
277       LanguageResource res = lr;
278 
279       if (lr instanceof Corpus) {
280         FeatureMap features1 = Factory.newFeatureMap();
281         features1.put("transientSource", lr);
282         try {
283           //here we create the persistent LR via Factory, so it's registered
284           //in GATE
285           res = (LanguageResource)
286             Factory.createResource("gate.corpora.SerialCorpusImpl", features1);
287           //Here the transient corpus is not deleted from the CRI, because
288           //this might not always be the desired behaviour
289           //since we chose that it is for the GUI, this functionality is
290           //now move to the 'Save to' action code in NameBearerHandle
291         catch (gate.creole.ResourceInstantiationException ex) {
292           throw new GateRuntimeException(ex.getMessage());
293         }
294 
295       }
296 
297       res.setDataStore(this);
298 
299       // let the world know
300       fireResourceAdopted(
301           new DatastoreEvent(this, DatastoreEvent.RESOURCE_ADOPTED, lr, null)
302       );
303       return res;
304     else if(currentDS.equals(this))         // adopted already here
305       return lr;
306     else {                      // someone else's child
307       throw new PersistenceException(
308         "Can't adopt a resource which is already in a different datastore"
309       );
310     }
311 
312 
313   // adopt(LR)
314 
315   /** Open a connection to the data store. */
316   @Override
317   public void open() throws PersistenceException {
318     if(storageDir == null)
319       throw new PersistenceException("Can't open: storage dir is null");
320 
321     // check storage directory is readable
322     if(! storageDir.canRead()) {
323       throw new PersistenceException("Can't read " + storageDir);
324     }
325 
326     // check storage directory is a valid serial datastore
327 // if we want to support old style:
328 // String versionInVersionFile = "1.0";
329 // (but this means it will open *any* directory)
330     try {
331       FileReader fis = new FileReader(getVersionFile());
332       BufferedReader isr = new BufferedReader(fis);
333       currentProtocolVersion = isr.readLine();
334       if(DEBUGOut.prln("opening SDS version " + currentProtocolVersion);
335       isr.close();
336     catch(IOException e) {
337       throw new PersistenceException(
338         "Invalid storage directory: " + e
339       );
340     }
341     if(! isValidProtocolVersion(currentProtocolVersion))
342       throw new PersistenceException(
343         "Invalid protocol version number: " + currentProtocolVersion
344       );
345 
346   // open()
347 
348   /** Close the data store. */
349   @Override
350   public void close() throws PersistenceException {
351     Gate.getDataStoreRegister().remove(this);
352   // close()
353 
354   /** Save: synchonise the in-memory image of the LR with the persistent
355     * image.
356     */
357   @Override
358   public void sync(LanguageResource lrthrows PersistenceException {
359 //    Out.prln("SDS: LR sync called. Saving " + lr.getClass().getName());
360 
361     // check that this LR is one of ours (i.e. has been adopted)
362     if(lr.getDataStore() == null || ! lr.getDataStore().equals(this))
363       throw new PersistenceException(
364         "LR " + lr.getName() " has not been adopted by this DataStore"
365       );
366 
367     // find the resource data for this LR
368     ResourceData lrData =
369       Gate.getCreoleRegister().get(lr.getClass().getName());
370 
371     // create a subdirectory for resources of this type if none exists
372     File resourceTypeDirectory = new File(storageDir, lrData.getClassName());
373     if(
374       (! resourceTypeDirectory.exists()) ||
375       (! resourceTypeDirectory.isDirectory())
376     ) {
377       // try to create the directory, throw an exception if it does not
378       // exist after this attempt.  It is possible for mkdir to fail and exists
379       // still to return true if another thread managed to sneak in and
380       // create the directory in the meantime
381       if(! resourceTypeDirectory.mkdir() && ! resourceTypeDirectory.exists())
382         throw new PersistenceException("Can't write " + resourceTypeDirectory);
383     }
384 
385     // create an indentifier for this resource
386     String lrName = null;
387     Object lrPersistenceId = null;
388     lrName = lr.getName();
389     lrPersistenceId = lr.getLRPersistenceId();
390 
391     if(lrName == null)
392       lrName = lrData.getName();
393     if(lrPersistenceId == null) {
394       lrPersistenceId = constructPersistenceId(lrName);
395       lr.setLRPersistenceId(lrPersistenceId);
396     }
397 
398     //we're saving a corpus. I need to save its documents first
399     if (lr instanceof Corpus) {
400       //check if the corpus is the one we support. CorpusImpl cannot be saved!
401       if ((lr instanceof SerialCorpusImpl))
402         throw new PersistenceException("Can't save a corpus which " +
403                                        "is not of type SerialCorpusImpl!");
404       SerialCorpusImpl corpus = (SerialCorpusImpllr;
405       //this is a list of the indexes of all newly-adopted documents
406       //which will be used by the SerialCorpusImpl to update the
407       //corresponding document IDs
408       for (int i = 0; i < corpus.size(); i++) {
409         //if the document is not in memory, there's little point in saving it
410         if ( (!corpus.isDocumentLoaded(i)) && corpus.isPersistentDocument(i))
411           continue;
412         if (DEBUG)
413           Out.prln("Saving document at position " + i);
414         if (DEBUG)
415           Out.prln("Document in memory " + corpus.isDocumentLoaded(i));
416         if (DEBUG)
417           Out.prln("is persistent? "+ corpus.isPersistentDocument(i));
418         if (DEBUG)
419           Out.prln("Document name at position" + corpus.getDocumentName(i));
420         Document doc = corpus.get(i);
421         try {
422           //if the document is not already adopted, we need to do that first
423           if (doc.getLRPersistenceId() == null) {
424             if (DEBUGOut.prln("Document adopted" + doc.getName());
425             doc = (Documentthis.adopt(doc);
426             this.sync(doc);
427             if (DEBUGOut.prln("Document sync-ed");
428             corpus.setDocumentPersistentID(i, doc.getLRPersistenceId());
429           else{
430             //if it is adopted, just sync it
431             this.sync(doc);
432             if (DEBUGOut.prln("Document sync-ed");
433           }
434           // store the persistent ID. Needs to be done even if the document was
435           //already adopted, in case the doc was already persistent 
436           //when added to the corpus
437           corpus.setDocumentPersistentID(i, doc.getLRPersistenceId());
438           if (DEBUGOut.prln("new document ID " + doc.getLRPersistenceId());
439         catch (Exception ex) {
440           throw new PersistenceException("Error while saving corpus: "
441                                          + corpus
442                                          "because of an error storing document "
443                                          + ex.getMessage(), ex);
444         }
445       }//for loop through documents
446     }
447 
448     // create a File to store the resource in
449     File resourceFile = new File(resourceTypeDirectory, (StringlrPersistenceId);
450 
451     // dump the LR into the new File
452     try {
453       OutputStream os = new FileOutputStream(resourceFile);
454 
455       // after 1.1 the serialised files are compressed
456       if(! currentProtocolVersion.equals("1.0"))
457         os = new GZIPOutputStream(os);
458 
459       os=new BufferedOutputStream(os);
460       
461       ObjectOutputStream oos = new ObjectOutputStream(os);
462       oos.writeObject(lr);
463       oos.close();
464     catch(IOException e) {
465       throw new PersistenceException("Couldn't write to storage file: " + e.getMessage(),e);
466     }
467 
468     // let the world know about it
469     fireResourceWritten(
470       new DatastoreEvent(
471         this, DatastoreEvent.RESOURCE_WRITTEN, lr, lrPersistenceId
472       )
473     );
474   // sync(LR)
475 
476   /** Create a persistent store Id from the name of a resource. */
477   protected String constructPersistenceId(String lrName) {
478     // change the persistence ID so that it can be used as a filename
479     lrName =
480         lrName.substring(0, Math.min(50, lrName.length())).replaceAll(
481             "[\\/:\\*\\?\"<>|]""_");
482     return lrName + "___" new Date().getTime() "___" + random();
483   // constructPersistenceId
484 
485   @Override
486   public LanguageResource getLr(String lrClassName, Object lrPersistenceId)
487   throws PersistenceException,SecurityException {
488 
489     // find the subdirectory for resources of this type
490     File resourceTypeDirectory = new File(storageDir, lrClassName);
491     if(
492       (! resourceTypeDirectory.exists()) ||
493       (! resourceTypeDirectory.isDirectory())
494     ) {
495         throw new PersistenceException("Can't find " + resourceTypeDirectory);
496     }
497 
498     // create a File to representing the resource storage file
499     File resourceFile = new File(resourceTypeDirectory, lrPersistenceId.toString());
500     if(! resourceFile.exists() || ! resourceFile.isFile())
501       throw new PersistenceException("Can't find file " + resourceFile);
502 
503     // try and read the file and deserialise it
504     LanguageResource lr = null;
505     try {
506       InputStream is = new FileInputStream(resourceFile);
507 
508       // after 1.1 the serialised files are compressed
509       if(! currentProtocolVersion.equals("1.0"))
510         is = new GZIPInputStream(is);
511 
512       is=new BufferedInputStream(is);
513       
514       // Use an input stream that is aware of the GATE classloader
515       ObjectInputStream ois = new GateAwareObjectInputStream(is);
516       lr = (LanguageResourceois.readObject();
517       ois.close();
518     catch(IOException e) {
519       throw
520         new PersistenceException("Couldn't read file "+resourceFile+": "+e);
521     catch(ClassNotFoundException ee) {
522       throw
523         new PersistenceException("Couldn't find class "+lrClassName+": "+ee);
524     }
525 
526     // set the dataStore property of the LR (which is transient and therefore
527     // not serialised)
528     lr.setDataStore(this);
529     lr.setLRPersistenceId(lrPersistenceId);
530 
531     if (DEBUGOut.prln("LR read in memory: " + lr);
532 
533     return lr;
534   // getLr(id)
535 
536   /** Get a list of the types of LR that are present in the data store. */
537   @Override
538   public List<String> getLrTypes() throws PersistenceException {
539     if(storageDir == null || ! storageDir.exists())
540       throw new PersistenceException("Can't read storage directory");
541 
542     // filter out the version file
543     String[] fileArray = filterIgnoredFileNames(storageDir.list());
544     List<String> lrTypes = new ArrayList<String>();
545     for(int i=0; i<fileArray.length; i++)
546       if(! fileArray[i].equals(versionFileName))
547         lrTypes.add(fileArray[i]);
548 
549     return lrTypes;
550   // getLrTypes()
551 
552   /** Get a list of the IDs of LRs of a particular type that are present. */
553   @Override
554   public List<String> getLrIds(String lrTypethrows PersistenceException {
555     // a File to represent the directory for this type
556     File resourceTypeDir = new File(storageDir, lrType);
557     if(! resourceTypeDir.exists())
558       return Arrays.asList(new String[0]);
559 
560     return Arrays.asList(filterIgnoredFileNames(resourceTypeDir.list()));
561   // getLrIds(lrType)
562 
563   /** Get a list of the names of LRs of a particular type that are present. */
564   @Override
565   public List<String> getLrNames(String lrTypethrows PersistenceException {
566     // the list of files storing LRs of this type; an array for the names
567     List<String> lrFileNames = getLrIds(lrType);
568     List<String> lrNames = new ArrayList<String>();
569 
570     // for each lr file name, munge its name and add to the lrNames list
571     for(String fname : lrFileNames) {
572       lrNames.add(getLrName(fname));
573     }
574 
575     return lrNames;
576   // getLrNames(lrType)
577 
578   /** Get the name of an LR from its ID. */
579   @Override
580   public String getLrName(Object lrId) {
581     String sLRid = lrId.toString();
582     int secondSeparator = sLRid.lastIndexOf("___");
583     sLRid = sLRid.substring(0, secondSeparator);
584     int firstSeparator = sLRid.lastIndexOf("___");
585     return sLRid.substring(0, firstSeparator);
586   // getLrName
587 
588   /** Set method for the autosaving behaviour of the data store.
589     <B>NOTE:</B> this type of datastore has no auto-save function,
590     * therefore this method throws an UnsupportedOperationException.
591     */
592   @Override
593   public void setAutoSaving(boolean autoSaving)
594   throws UnsupportedOperationException {
595     throw new UnsupportedOperationException(
596       "SerialDataStore has no auto-save capability"
597     );
598   // setAutoSaving
599 
600   /** Get the autosaving behaviour of the LR. */
601   @Override
602   public boolean isAutoSaving() { return autoSaving; }
603 
604   /** Flag for autosaving behaviour. */
605   protected boolean autoSaving = false;
606 
607   /** Generate a random integer between 0 and 9999 for file naming. */
608   protected static int random() {
609     return randomiser.nextInt(9999);
610   // random
611 
612   /** Random number generator */
613   private static final Random randomiser = new Random();
614   private transient Vector<DatastoreListener> datastoreListeners;
615 
616   /** String representation */
617   @Override
618   public String toString() {
619     String nl = Strings.getNl();
620     StringBuffer s = new StringBuffer("SerialDataStore: ");
621     s.append("autoSaving: " + autoSaving);
622     s.append("; storageDir: " + storageDir);
623     s.append(nl);
624 
625     return s.toString();
626   // toString()
627 
628   /** Calculate a hash code based on the class and the storage dir. */
629   @Override
630   public int hashCode(){
631     return getClass().hashCode() ^ storageDir.hashCode();
632   // hashCode
633 
634   /** Equality: based on storage dir of other. */
635   @Override
636   public boolean equals(Object other) {
637 
638 
639     if ((other instanceof SerialDataStore))
640       return false;
641 
642     if (((SerialDataStore)other).storageDir.equals(storageDir))
643       return false;
644 
645     //check for the name. First with equals, because they can be both null
646     //in which case trying just with equals leads to a null pointer exception
647     if (((SerialDataStore)other).name == name)
648       return true;
649     else
650       return ((SerialDataStore)other).name.equals(name);
651   // equals
652 
653   @Override
654   public synchronized void removeDatastoreListener(DatastoreListener l) {
655     if (datastoreListeners != null && datastoreListeners.contains(l)) {
656       @SuppressWarnings("unchecked")
657       Vector<DatastoreListener> v = (Vector<DatastoreListener>datastoreListeners.clone();
658       v.removeElement(l);
659       datastoreListeners = v;
660     }
661   }
662   @Override
663   public synchronized void addDatastoreListener(DatastoreListener l) {
664     @SuppressWarnings("unchecked")
665     Vector<DatastoreListener> v = datastoreListeners == null new Vector<DatastoreListener>(2(Vector<DatastoreListener>datastoreListeners.clone();
666     if (!v.contains(l)) {
667       v.addElement(l);
668       datastoreListeners = v;
669     }
670   }
671   protected void fireResourceAdopted(DatastoreEvent e) {
672     if (datastoreListeners != null) {
673       Vector<DatastoreListener> listeners = datastoreListeners;
674       int count = listeners.size();
675       for (int i = 0; i < count; i++) {
676         listeners.elementAt(i).resourceAdopted(e);
677       }
678     }
679   }
680   protected void fireResourceDeleted(DatastoreEvent e) {
681     if (datastoreListeners != null) {
682       Vector<DatastoreListener> listeners = datastoreListeners;
683       int count = listeners.size();
684       for (int i = 0; i < count; i++) {
685         listeners.elementAt(i).resourceDeleted(e);
686       }
687     }
688   }
689   protected void fireResourceWritten(DatastoreEvent e) {
690     if (datastoreListeners != null) {
691       Vector<DatastoreListener> listeners = datastoreListeners;
692       int count = listeners.size();
693       for (int i = 0; i < count; i++) {
694         listeners.elementAt(i).resourceWritten(e);
695       }
696     }
697   }
698 
699   /**
700    * Returns the name of the icon to be used when this datastore is displayed
701    * in the GUI
702    */
703   @Override
704   public String getIconName(){
705     return "datastore";
706   }
707 
708   /**
709    * Returns the comment displayed by the GUI for this DataStore
710    */
711   @Override
712   public String getComment(){
713     return "GATE serial datastore";
714   }
715 
716   /**
717    * Checks if the user (identified by the sessionID)
718    *  has read access to the LR
719    */
720   @Override
721   public boolean canReadLR(Object lrID)
722     throws PersistenceException{
723 
724     return true;
725   }
726   /**
727    * Checks if the user (identified by the sessionID)
728    * has write access to the LR
729    */
730   @Override
731   public boolean canWriteLR(Object lrID)
732     throws PersistenceException{
733 
734     return true;
735   }
736 
737     /** Sets the name of this resource*/
738   @Override
739   public void setName(String name){
740     this.name = name;
741   }
742 
743   /** Returns the name of this resource*/
744   @Override
745   public String getName(){
746     return name;
747   }
748 
749   /**
750    * Try to acquire exlusive lock on a resource from the persistent store.
751    * Always call unlockLR() when the lock is no longer needed
752    */
753   @Override
754   public boolean lockLr(LanguageResource lr)
755   throws PersistenceException,SecurityException {
756     return true;
757   }
758 
759   /**
760    * Releases the exlusive lock on a resource from the persistent store.
761    */
762   @Override
763   public void unlockLr(LanguageResource lr)
764   throws PersistenceException,SecurityException {
765     return;
766   }
767 
768   /** Get a list of LRs that satisfy some set or restrictions */
769   @SuppressWarnings("rawtypes")
770   @Override
771   public List findLrIds(List constraintsthrows PersistenceException {
772     throw new UnsupportedOperationException(
773                               "Serial DataStore does not support document retrieval.");
774   }
775 
776   /**
777    *  Get a list of LRs that satisfy some set or restrictions and are
778    *  of a particular type
779    */
780   @SuppressWarnings("rawtypes")
781   @Override
782   public List findLrIds(List constraints, String lrTypethrows PersistenceException {
783     throw new UnsupportedOperationException(
784                               "Serial DataStore does not support document retrieval.");
785   }
786 
787   /**
788    * This removes the names of all files from a list of file names for which
789    * we know that we want to ignore them.
790    * Currently, all names starting with a dot are ignored.
791    
792    @param fileNames
793    @return the list of file names with the ignored names removed
794    */
795   protected String[] filterIgnoredFileNames(String[] fileNames) {
796     if(fileNames == null || fileNames.length == 0) {
797       return fileNames;
798     }
799     Vector<String> filteredNames = new Vector<String>(fileNames.length);
800     for(String filname : fileNames) {
801       if(!filname.startsWith(".")) {
802         filteredNames.add(filname);
803       }
804     }
805     return filteredNames.toArray(new String[0]);
806   }
807 
808 
809 // class SerialDataStore