package org.juxtasoftware.resource; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.juxtasoftware.dao.ComparisonSetDao; import org.juxtasoftware.dao.JuxtaAnnotationDao; import org.juxtasoftware.dao.QNameFilterDao; import org.juxtasoftware.dao.WitnessDao; import org.juxtasoftware.model.AnnotationConstraint; import org.juxtasoftware.model.ComparisonSet; import org.juxtasoftware.model.JuxtaAnnotation; import org.juxtasoftware.model.QNameFilter; import org.juxtasoftware.model.Witness; import org.restlet.data.Status; import org.restlet.representation.Representation; import org.restlet.resource.Get; import org.restlet.resource.Post; import org.restlet.resource.ResourceException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import eu.interedition.text.Name; import eu.interedition.text.NameRepository; import eu.interedition.text.Range; import eu.interedition.text.Text; import eu.interedition.text.mem.SimpleName; @Service @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class AnnotationsResource extends BaseResource { @Autowired private ComparisonSetDao setDao; @Autowired private QNameFilterDao filterDao; @Autowired private WitnessDao witnessDao; @Autowired private JuxtaAnnotationDao annotationDao; @Autowired private NameRepository qnameRepo; private ComparisonSet set = null; private Witness witness = null; private boolean includeText = false; private QNameFilter filter = null; private Range range; private Gson gson; @Override protected void doInit() throws ResourceException { super.doInit(); // get the set and validate that it exists and is in the wprkspace Long setId = getIdFromAttributes("setId"); if ( setId == null ) { return; } this.set = this.setDao.find( setId); if ( validateModel(this.set) == false ) { return; } // once the set is workspace validated, just make sure the witness // exists and is part of the set Long witnessId = getIdFromAttributes("witnessId"); if ( witnessId == null ) { return; } this.witness = this.witnessDao.find( witnessId); if ( witness == null ) { setStatus(Status.CLIENT_ERROR_NOT_FOUND, "Invalid witness identifier specified"); return; } if ( this.setDao.isWitness(this.set, this.witness) == false ) { setStatus(Status.CLIENT_ERROR_NOT_FOUND, "Witness "+witnessId+" does not exist in set "+setId); return; } // content requested? if (getQuery().getValuesMap().containsKey("content") ) { this.includeText = true; } // filter results? if (getQuery().getValuesMap().containsKey("filter") ) { String filterName = getQuery().getValuesMap().get("filter"); this.filter = this.filterDao.find(filterName); if ( this.filter == null ) { setStatus(Status.CLIENT_ERROR_NOT_FOUND, "Invalid filter specified"); } } // was a range set requested? if (getQuery().getValuesMap().containsKey("range") ) { String rangeInfo = getQuery().getValues("range"); String ranges[] = rangeInfo.split(","); if ( ranges.length == 2) { this.range = new Range( Integer.parseInt(ranges[0]), Integer.parseInt(ranges[1]) ); } else { setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "Invalid Range specified"); } } this.gson = new GsonBuilder() .setExclusionStrategies( new TextExclusion() ) .create(); } @Get("html") public Representation toHtml() { List<JuxtaAnnotation> list = getAnnotations(); Map<String,Object> map = new HashMap<String,Object>(); map.put("setId", this.set.getId()); map.put("witness", this.witness ); map.put("annotations", list ); map.put("includeText", this.includeText); map.put("page", "set"); map.put("title", "Juxta \""+this.witness.getName()+"\" Annotations"); return toHtmlRepresentation("annotations.ftl",map); } @Get("json") public Representation toJson() { List<JuxtaAnnotation> list = getAnnotations(); String json = this.gson.toJson( list ); return toJsonRepresentation(json); } private List<JuxtaAnnotation> getAnnotations() { // build the constraints for the request; range, text and qname filter AnnotationConstraint constraint = new AnnotationConstraint( this.set.getId(), this.witness ); constraint.setIncludeText( this.includeText ); constraint.addRange( this.range ); constraint.setFilter( this.filter ); // get the constrained list of anotation data; convert to json List<JuxtaAnnotation> list = this.annotationDao.list( constraint ); return list; } @Post("json") public Representation fromJson( final String jsonStr ) { LOG.info("Create annotation from "+jsonStr); JsonParser parser = new JsonParser(); JsonArray array = null; try { array = parser.parse(jsonStr).getAsJsonArray(); } catch ( Exception e ) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return toTextRepresentation("Invalid json payload"); } List<JuxtaAnnotation> annotations = new ArrayList<JuxtaAnnotation>(); for ( Iterator<JsonElement> itr = array.iterator(); itr.hasNext(); ) { JsonObject annoObj = itr.next().getAsJsonObject(); // get name Name qname = null; Range range =null; try { JsonObject nameObj = annoObj.get("name").getAsJsonObject(); qname = this.qnameRepo.get( new SimpleName( nameObj.get("namespace").getAsString(), nameObj.get("localName").getAsString() ) ); JsonObject rangeObj = annoObj.get("range").getAsJsonObject(); range = new Range(rangeObj.get("start").getAsLong(), rangeObj.get("end").getAsLong()); } catch (Exception e) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return toTextRepresentation("Invalid json payload"); } JuxtaAnnotation ano = new JuxtaAnnotation( this.set.getId(), witness, qname, range); ano.setManual(); annotations.add(ano); } Integer cnt = this.annotationDao.create(annotations); return toTextRepresentation( cnt.toString() ); } private static class TextExclusion implements ExclusionStrategy { @Override public boolean shouldSkipField(FieldAttributes f) { return false; } @Override public boolean shouldSkipClass(Class<?> clazz) { return (clazz == Text.class); } } }