package nl.ipo.cds.metadata;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPathExpressionException;
import nl.ipo.cds.domain.MetadataDocumentType;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class MetadataManager {
private static final Log logger = LogFactory.getLog(MetadataManager.class);
protected final File metadataFolder;
protected final Schema xmlSchema;
public MetadataManager(final File metadataFolder) throws IOException, SAXException {
final String metadataFolderPath = metadataFolder.getCanonicalPath();
logger.debug("Constructing MetadataManager with metadata folder: " + metadataFolderPath);
if(!metadataFolder.exists()) {
throw new IllegalArgumentException("Metadata folder doesn't exist: " + metadataFolderPath);
}
if(!metadataFolder.isDirectory()) {
throw new IllegalArgumentException("Metadata folder parameter should refer to a directory: " + metadataFolderPath);
}
this.metadataFolder = metadataFolder;
final JarResourceResolver resolver = new JarResourceResolver("META-INF/schemas");
resolver.addPath("http://schemas.opengis.net/iso/19139/20060504/", "19139/");
resolver.addPath("http://www.w3.org/1999/xlink.xsd", "xlink/xlink.xsd");
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setResourceResolver(resolver);
xmlSchema = schemaFactory.newSchema(new Source[]{
new StreamSource("http://schemas.opengis.net/iso/19139/20060504/gmd/gmd.xsd"),
new StreamSource("http://schemas.opengis.net/iso/19139/20060504/srv/srv.xsd")});
}
protected File getDocumentFile(final String documentName) {
return new File(metadataFolder, documentName);
}
public synchronized void updateMetadata(final String documentName, final MetadataDocumentType documentType, final String dateTime) throws IOException {
logger.debug("Updating metadata document: '" + documentName + "' dateTime: " + dateTime);
final File f = getDocumentFile(documentName);
if(!f.exists()) {
throw new IllegalArgumentException("Metadata document doesn't exists.");
}
try {
XMLRewriter rewriter = createRewriter(new FileInputStream(f));
updateMetadata(documentType, dateTime, rewriter);
FileOutputStream outputStream = new FileOutputStream(f);
rewriter.write(outputStream);
outputStream.close();
} catch(Exception e) {
throw new IOException("Couldn't update metadata document");
}
}
private void updateMetadata(final MetadataDocumentType documentType,
final String dateTime, XMLRewriter rewriter)
throws XPathExpressionException {
if(documentType == MetadataDocumentType.SERVICE) {
updateServiceMetadata(rewriter, dateTime);
} else if(documentType == MetadataDocumentType.DATASET){
updateDatasetMetadata(rewriter, dateTime);
} else {
throw new IllegalArgumentException("Unknown type: " + documentType);
}
}
public synchronized byte[] retrieveDocument(final String documentName) throws IOException {
final File f = getDocumentFile(documentName);
if(f.exists()) {
final FileInputStream fis = new FileInputStream(f);
byte[] retval = IOUtils.toByteArray(fis);
fis.close();
return retval;
} else {
throw new IllegalArgumentException("Document doesn't exists: " + documentName);
}
}
public synchronized boolean documentExists(final String documentName) {
final File f = getDocumentFile(documentName);
return f.exists();
}
public ValidationResult validateDocument(final byte[] bytes, final MetadataDocumentType documentType) {
final Document document;
try {
document = createDocument(new ByteArrayInputStream(bytes));
} catch(Exception e) {
return ValidationResult.NOT_WELL_FORMED;
}
try {
Validator validator = xmlSchema.newValidator();
validator.validate(new DOMSource(document));
} catch(Exception e) {
return ValidationResult.SCHEMA_VIOLATION;
}
try {
final XMLRewriter rewriter = createRewriter(document);
updateMetadata(documentType, "", rewriter);
} catch(Exception e) {
return ValidationResult.DATE_PATH_MISSING;
}
return ValidationResult.VALID;
}
public synchronized void storeDocument(final String documentName, final byte[] bytes) throws ParserConfigurationException, SAXException, IOException, TransformerException {
final File f = getDocumentFile(documentName);
final FileOutputStream fos = new FileOutputStream(f);
fos.write(bytes);
fos.close();
}
public synchronized void deleteDocument(final String documentName) {
final File f = new File(metadataFolder, documentName);
if(f.exists()) {
f.delete();
} else {
throw new IllegalArgumentException("Document doesn't exists: " + documentName);
}
}
public Set<String> listDocuments() {
final Set<String> documents = new HashSet<String>();
for(final String fileName : metadataFolder.list()) {
if(fileName.toLowerCase().endsWith(".xml")) {
documents.add(fileName);
}
}
return documents;
}
protected XMLRewriter createRewriter(final Document document) throws ParserConfigurationException, SAXException, IOException {
XMLRewriter rewriter = new XMLRewriter(document);
rewriter.addNamespace("gmd", "http://www.isotc211.org/2005/gmd");
rewriter.addNamespace("gco", "http://www.isotc211.org/2005/gco");
rewriter.addNamespace("srv", "http://www.isotc211.org/2005/srv");
rewriter.addNamespace("gml", "http://www.opengis.net/gml");
return rewriter;
}
protected Document createDocument(final InputStream inputStream) throws IOException, ParserConfigurationException, SAXException {
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
final DocumentBuilder db = dbf.newDocumentBuilder();
final Document document = db.parse(inputStream);
inputStream.close();
return document;
}
protected XMLRewriter createRewriter(final InputStream inputStream) throws ParserConfigurationException, SAXException, IOException {
return createRewriter(createDocument(inputStream));
}
protected void updateServiceMetadata(final XMLRewriter rewriter, final String dateTime) throws XPathExpressionException {
rewriter.modify("/gmd:MD_Metadata/gmd:identificationInfo/srv:SV_ServiceIdentification/srv:extent/gmd:EX_Extent/gmd:temporalElement/gmd:EX_TemporalExtent/gmd:extent/gml:TimePeriod/gml:begin/gml:TimeInstant/gml:timePosition", dateTime);
}
protected void updateDatasetMetadata(final XMLRewriter rewriter, final String dateTime) throws XPathExpressionException {
rewriter.modify("/gmd:MD_Metadata/gmd:identificationInfo/gmd:MD_DataIdentification/gmd:citation/gmd:CI_Citation/gmd:date/gmd:CI_Date[gmd:dateType/gmd:CI_DateTypeCode/@codeListValue = 'revision']/gmd:date/gco:DateTime", dateTime);
rewriter.modify("/gmd:MD_Metadata/gmd:identificationInfo/gmd:MD_DataIdentification/gmd:extent/gmd:EX_Extent/gmd:temporalElement/gmd:EX_TemporalExtent/gmd:extent/gml:TimePeriod/gml:begin/gml:TimeInstant/gml:timePosition", dateTime);
}
}