package nl.knaw.huygens.alexandria.endpoint.iiif;
/*
* #%L
* alexandria-main
* =======
* Copyright (C) 2015 - 2017 Huygens ING (KNAW)
* =======
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import nl.knaw.huygens.alexandria.endpoint.webannotation.WebAnnotationService;
import nl.knaw.huygens.alexandria.exception.BadRequestException;
public class AnnotationListHandler {
private JsonParser jParser;
private String context;
private Deque<String> deque = new ConcurrentLinkedDeque<>();
private AtomicBoolean firstField = new AtomicBoolean(true);
private AtomicBoolean inResources = new AtomicBoolean(false);
private WebAnnotationService webAnnotationService;
public AnnotationListHandler(InputStream inputStream, WebAnnotationService webAnnotationService) {
this.webAnnotationService = webAnnotationService;
try {
jParser = new JsonFactory().createParser(inputStream);
deque.add("{");
} catch (IOException e) {
e.printStackTrace();
}
}
public String getNextString() {
try {
if (inResources.get()) {
handleNextResource();
} else {
handleRootFields();
}
} catch (IOException e) {
e.printStackTrace();
}
return deque.isEmpty() ? null : deque.pop();
}
private void handleRootFields() throws IOException, JsonProcessingException {
if (jParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jParser.getCurrentName();
if (fieldname != null) {
if (!firstField.get()) {
deque.add(",");
}
firstField.set(false);
switch (fieldname) {
case "@context":
jParser.nextToken();
context = jParser.getText(); // we'll be needing this later
deque.add("\"@context\":\"" + context + "\"");
break;
case "resources":
if (context == null) {
throw new BadRequestException("Missing @context field, should be defined at the start of the json payload.");
}
deque.add("\"resources\":[");
jParser.nextToken(); // "["
// parse each resource
firstField.set(true);
inResources.set(true);
break;
default:
jParser.nextToken();
deque.add("\"" + fieldname + "\":");
JsonToken currentToken = jParser.currentToken();
if (currentToken.isStructStart()) {
deque.add(new ObjectMapper().readTree(jParser).toString());
} else if (currentToken.isNumeric()) {
deque.add(jParser.getValueAsString());
} else {
deque.add("\"" + jParser.getText() + "\"");
}
break;
}
}
} else {
close();
}
}
private void handleNextResource() throws IOException, JsonProcessingException {
if (jParser.nextToken() != JsonToken.END_ARRAY) {
if (!firstField.get()) {
deque.add(",");
}
firstField.set(false);
ObjectNode annotationNode = new ObjectMapper().readTree(jParser);
String created = Instant.now().toString();
annotationNode.set("http://purl.org/dc/terms/created", new TextNode(created));
annotationNode.set("@context", new TextNode(context));
ObjectNode storedAnnotation = webAnnotationService.validateAndStore(annotationNode);
deque.add(new ObjectMapper().writeValueAsString(storedAnnotation));
} else {
deque.add("]");
inResources.set(false);
}
}
private void close() {
try {
jParser.close();
deque.add("}");
} catch (IOException e) {
e.printStackTrace();
}
}
}