/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.module.drawing.obs.handler;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Obs;
import org.openmrs.User;
import org.openmrs.api.context.Context;
import org.openmrs.module.drawing.AnnotatedImage;
import org.openmrs.module.drawing.ImageAnnotation;
import org.openmrs.module.drawing.ImageAnnotation.Status;
import org.openmrs.module.drawing.Position;
import org.openmrs.obs.ComplexData;
import org.openmrs.obs.handler.ImageHandler;
import org.openmrs.web.WebConstants;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
*
*/
public class DrawingHandler extends ImageHandler {
private Log log = LogFactory.getLog(DrawingHandler.class);
/**
* @see org.openmrs.obs.handler.ImageHandler#saveObs(org.openmrs.Obs)
*/
@Override
public Obs saveObs(Obs obs) {
ComplexData c = obs.getComplexData();
AnnotatedImage ai = (AnnotatedImage) c.getData();
obs.setComplexData(new ComplexData(c.getTitle(), ai.getImage()));
Obs o = super.saveObs(obs);
for (ImageAnnotation annotation : ai.getAnnotations())
saveAnnotation(o, annotation, annotation.getStatus() == Status.DELETE);
log.info("drawing:saving complexObs:" + o);
return o;
}
public Obs getObs(Obs obs, String view) {
File imageFile = getComplexDataFile(obs);
BufferedImage img = null;
try {
img = ImageIO.read(imageFile);
}
catch (IOException e) {
log.error("Trying to read file: " + imageFile.getAbsolutePath(), e);
}
AnnotatedImage aimage = loadMetadata(obs, new AnnotatedImage(img));
String url = "/" + WebConstants.WEBAPP_NAME + "/module/drawing/manage.form?obsId=" + obs.getId();
if (view == WebConstants.HYPERLINK_VIEW) {
obs.setComplexData(new ComplexData(imageFile.getName(), url));
} else if (view == WebConstants.HTML_VIEW) {
String html = "<a href=\"" + url + "\">" + imageFile.getName() + "</a>";
obs.setComplexData(new ComplexData(imageFile.getName(), html));
} else {
obs.setComplexData(new ComplexData(imageFile.getName(), aimage));
}
return obs;
}
/**
* Parses the XML metadata file (if it exists) loads the metadata into the given AnnotatedImage
* and returns it.
*
* @param obs
*/
public AnnotatedImage loadMetadata(Obs obs, AnnotatedImage image) {
File metadataFile = getComplexMetadataFile(obs);
image.setHandler(this);
ArrayList<ImageAnnotation> annotations = new ArrayList<ImageAnnotation>();
if (metadataFile.exists() && metadataFile.canRead()) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document xmldoc = builder.parse(metadataFile);
NodeList annotationNodeList = xmldoc.getElementsByTagName("Annotation");
for (int i = 0; i < annotationNodeList.getLength(); i++) {
try {
Node node = annotationNodeList.item(i);
NamedNodeMap attributes = node.getAttributes();
String text = node.getTextContent();
String idString = attributes.getNamedItem("id").getNodeValue();
String date = attributes.getNamedItem("date").getNodeValue();
String userid = attributes.getNamedItem("userid").getNodeValue();
String xcoordinate = attributes.getNamedItem("xcoordinate").getNodeValue();
String ycoordinate = attributes.getNamedItem("ycoordinate").getNodeValue();
Position position = new Position(Integer.parseInt(xcoordinate), Integer.parseInt(ycoordinate));
User user = Context.getUserService().getUser(Integer.parseInt(userid));
annotations.add(new ImageAnnotation(Integer.parseInt(idString), position, text, new Date(Long
.parseLong(date)), user, Status.UNCHANGED));
}
catch (NumberFormatException e) {
// Skip that annotation
}
}
}
catch (Exception e) {
//Likely ParserConfigurationException, SAXException or IOException.
//Fail silently, log the error and return the image with no annotations.
log.error("Error loading annotations", e);
}
}
image.setAnnotations(annotations.toArray(new ImageAnnotation[0]));
return image;
}
public void saveAnnotation(Obs obs, ImageAnnotation annotation, boolean delete) {
try {
log.info("drawing: Saving annotation for obs " + obs.getObsId());
File metadataFile = getComplexMetadataFile(obs);
log.info("drawing: Using file " + metadataFile.getCanonicalPath());
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document xmldoc;
Element annotationsParent;
int newId = 0;
if (metadataFile.exists()) {
xmldoc = builder.parse(metadataFile);
annotationsParent = (Element) xmldoc.getElementsByTagName("Annotations").item(0);
NodeList annotationNodeList = xmldoc.getElementsByTagName("Annotation");
for (int i = 0; i < annotationNodeList.getLength(); i++) {
NamedNodeMap attributes = annotationNodeList.item(i).getAttributes();
String idString = attributes.getNamedItem("id").getNodeValue();
int existingId = Integer.parseInt(idString);
if (existingId == annotation.getId() && !(annotation.getStatus() == Status.UNCHANGED)) {
annotationsParent.removeChild(annotationNodeList.item(i));
break;
}
if (existingId >= newId)
newId = existingId + 1;
}
} else {
metadataFile.createNewFile();
DOMImplementation domImpl = builder.getDOMImplementation();
xmldoc = domImpl.createDocument(null, "ImageMetadata", null);
Element root = xmldoc.getDocumentElement();
annotationsParent = xmldoc.createElementNS(null, "Annotations");
root.appendChild(annotationsParent);
}
if (!delete && annotation.getStatus() != Status.UNCHANGED) {
if (annotation.getId() >= 0)
newId = annotation.getId();
Element e = xmldoc.createElementNS(null, "Annotation");
Node n = xmldoc.createTextNode(annotation.getText());
e.setAttributeNS(null, "id", newId + "");
e.setAttributeNS(null, "xcoordinate", annotation.getLocation().getX() + "");
e.setAttributeNS(null, "ycoordinate", annotation.getLocation().getY() + "");
e.setAttributeNS(null, "userid", annotation.getUser().getUserId() + "");
e.setAttributeNS(null, "date", annotation.getDate().getTime() + "");
e.appendChild(n);
annotationsParent.appendChild(e);
}
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(new DOMSource(xmldoc), new StreamResult(metadataFile));
log.info("drawing: Saving annotation complete");
}
catch (Exception e) {
log.error("drawing: Error saving image metadata: " + e.getClass() + " " + e.getMessage());
}
}
/**
* Convenience method to create and return a file for the stored metadata file
*
* @param obs
* @return
*/
public static File getComplexMetadataFile(Obs obs) {
File imageFile = ImageHandler.getComplexDataFile(obs);
try {
return new File(imageFile.getCanonicalPath() + ".xml");
}
catch (IOException e) {
return new File(imageFile.getAbsolutePath() + ".xml");
}
}
}