/*
* 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);
}
}
}