/* * Copyright (c) 2010, The University of Sheffield. * * This file is part of the GATE/Groovy integration layer, and is free * software, released under the terms of the GNU Lesser General Public * Licence, version 2.1 (or any later version). A copy of this licence * is provided in the file LICENCE in the distribution. * * Groovy is developed by The Codehaus, details are available from * http://groovy.codehaus.org */ package gate.groovy; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import gate.AnnotationSet; import gate.Corpus; import gate.Document; import gate.DocumentContent; import gate.Factory; import gate.Resource; import gate.SimpleAnnotationSet; import gate.util.InvalidOffsetException; import groovy.lang.Closure; import groovy.lang.Range; /** * Class containing static methods that will be mixed in to * several core GATE classes when the Groovy plugin is loaded, * making the methods available as instance methods on their * respective types (in the same way as Groovy does by default * for DefaultGroovyMethods). */ public class GateGroovyMethods { /** * Call the closure once for each document in this corpus, loading * and unloading documents as appropriate in the case of a persistent * corpus, and collecting the return values of each call into a list. * * @param self the corpus to traverse * @param closure the closure to call * @return a list of the return values from each closure call. */ public static <T> List<T> collect(Corpus self, Closure<T> closure) { return (List<T>)collect(self, new ArrayList<T>(), closure); } /** * Call the closure once for each document in this corpus, loading * and unloading documents as appropriate in the case of a persistent * corpus, and adding the return values of each call to the given * collection. * * @param self the corpus to traverse * @param closure the closure to call * @return a list of the return values from each closure call. */ public static <T> Collection<T> collect(Corpus self, Collection<T> coll, Closure<T> closure) { for(int i = 0; i < self.size(); i++) { boolean docWasLoaded = self.isDocumentLoaded(i); Document doc = self.get(i); coll.add(closure.call(doc)); if(!docWasLoaded) { self.unloadDocument(doc); Factory.deleteResource(doc); } } return coll; } /** * Call the closure once for each document in this corpus, loading * and unloading documents as appropriate in the case of a persistent * corpus. * * @param self the corpus to traverse * @param closure the closure to call * @return the corpus. */ public static <T> Object each(Corpus self, Closure<T> closure) { for(int i = 0; i < self.size(); i++) { boolean docWasLoaded = self.isDocumentLoaded(i); Document doc = self.get(i); closure.call(doc); if(!docWasLoaded) { self.unloadDocument(doc); Factory.deleteResource(doc); } } return self; } /** * Call the closure once for each document in this corpus, loading * and unloading documents as appropriate in the case of a persistent * corpus. The closure will receive two parameters, the document * and its Integer index in the corpus. * * @param self the corpus to traverse * @param closure the closure to call * @return the corpus. */ public static <T> Object eachWithIndex(Corpus self, Closure<T> closure) { for(int i = 0; i < self.size(); i++) { boolean docWasLoaded = self.isDocumentLoaded(i); Document doc = self.get(i); closure.call(new Object[] {doc, i}); if(!docWasLoaded) { self.unloadDocument(doc); Factory.deleteResource(doc); } } return self; } /** * Sub-range access for annotation sets (mapping to getContained). * Allows <code>someAnnotationSet[15..20]</code>. This works with ranges * whose end points are any numeric type, so as well as using integer * literals you can do <code>someAnnotationSet[ann.start()..ann.end()]</code> * (as start and end return Long). * @see AnnotationSet#getContained(Long, Long) */ @SuppressWarnings("unchecked") public static AnnotationSet getAt(AnnotationSet self, Range<?> range) { if(range.getFrom() instanceof Number) { return self.getContained( Long.valueOf(((Number)range.getFrom()).longValue()), Long.valueOf(((Number)range.getTo()).longValue())); } else if(range.getFrom() instanceof String) { return getAt(self, (List<String>)range); } else { throw new IllegalArgumentException( "AnnotationSet.getAt expects a numeric or string range"); } } /** * Sub-range access for document content. Allows * <code>documentContent[15..20]</code>. This works with ranges * whose end points are any numeric type, so as well as using integer * literals you can do <code>documentContent[ann.start()..ann.end()]</code> * (as start and end return Long). * @param self * @param range * @return */ public static DocumentContent getAt(DocumentContent self, Range<?> range) { if(range.getFrom() instanceof Number) { try { return self.getContent( Long.valueOf(((Number)range.getFrom()).longValue()), Long.valueOf(((Number)range.getTo()).longValue())); } catch(InvalidOffsetException ioe) { throw new IndexOutOfBoundsException(ioe.getMessage()); } } else { throw new IllegalArgumentException( "DocumentContent.getAt expects a numeric range"); } } /** * Array-style access for annotation sets. Allows things like * <code>someAnnotationSet["Token"]</code> * @see SimpleAnnotationSet#get(String) */ public static AnnotationSet getAt(SimpleAnnotationSet self, String type) { return self.get(type); } /** * Array-style access for annotation sets. Allows * <code>someAnnotationSet["Token", "SpaceToken"]</code> * @see SimpleAnnotationSet#get(Set) */ public static AnnotationSet getAt(SimpleAnnotationSet self, List<String> types) { return self.get(new HashSet<String>(types)); } /** * Call the given closure passing this resource as a parameter, * and ensuring that the resource is deleted when the closure * returns. This would typically be used in this kind of * construction: * <pre> * Factory.newDocument(someUrl).withResource { * // do something with the document (it) * } * </pre> * @param self * @param closure * @return the value returned from the closure */ public static <T> T withResource(Resource self, Closure<T> closure) { try { return closure.call(self); } finally { Factory.deleteResource(self); } } }