/**
* P4InformationPackage.java
* Author: Francesco Rosso (rosso@eurix.it)
* Contributors: Francesco Gallo (gallo@eurix.it)
*
* This file is part of PrestoPRIME Preservation Platform (P4).
*
* Copyright (C) 2009-2012 EURIX Srl, Torino, Italy
*
* 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/>.
*/
package eu.prestoprime.datamanagement.impl;
import it.eurix.archtools.data.model.DIP.DCField;
import it.eurix.archtools.data.model.InformationPackage;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import eu.prestoprime.conf.ConfigurationManager;
import eu.prestoprime.conf.P4PropertyManager.P4Property;
import eu.prestoprime.datamanagement.P4PersistenceManager.P4Collection;
import eu.prestoprime.model.ModelUtils;
import eu.prestoprime.model.ModelUtils.P4JAXBPackage;
import eu.prestoprime.model.P4NamespaceContext;
import eu.prestoprime.model.mets.AmdSecType;
import eu.prestoprime.model.mets.FileType;
import eu.prestoprime.model.mets.MdSecType;
import eu.prestoprime.model.mets.Mets;
import eu.prestoprime.model.mets.MetsType.FileSec;
import eu.prestoprime.model.mets.MetsType.FileSec.FileGrp;
public abstract class P4InformationPackage implements InformationPackage {
protected static final Logger logger = LoggerFactory.getLogger(P4InformationPackage.class);
private int counter;
protected final String id;
protected Node content;
protected List<P4Resource> resources;
public P4InformationPackage(String id, Node content) {
this.counter = 0;
this.id = id;
this.content = content;
this.resources = new ArrayList<>();
}
public int getCounter() {
return counter;
}
public void incrementCounter() {
counter++;
}
public void decrementCounter() {
counter--;
}
public Node getContent() {
return content;
}
public synchronized Mets getContentAsMets() throws P4IPException {
try {
return ((Mets) ModelUtils.getUnmarshaller(P4JAXBPackage.DATA_MODEL).unmarshal(content));
} catch (JAXBException e) {
e.printStackTrace();
throw new P4IPException("Unable to unmarshal content...");
}
}
public String getContentAsString(boolean indented) {
String xmlString = null;
try {
StringWriter sw = new StringWriter();
StreamResult result = new StreamResult(sw);
DOMSource source = new DOMSource(content);
TransformerFactory.newInstance().newTransformer().transform(source, result);
if (!indented) {
xmlString = sw.toString().replaceAll("(\\r|\\n)", "");
} else {
xmlString = sw.toString();
}
} catch (Exception e) {
e.printStackTrace();
}
return xmlString;
}
public synchronized void setContent(Mets mets) throws P4IPException {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Node node = dbf.newDocumentBuilder().newDocument();
ModelUtils.getMarshaller(P4JAXBPackage.DATA_MODEL).marshal(mets, node);
content = node;
try {
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.transform(new DOMSource(content), new StreamResult(System.out));
} catch (TransformerException te) {
logger.error("nodeToString Transformer Exception");
}
} catch (ParserConfigurationException | JAXBException e) {
e.printStackTrace();
throw new P4IPException("Unable to marshal mets...");
}
}
public abstract void selfRelease() throws P4IPException;
@Override
public String getId() {
return id;
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
try {
StreamResult result = new StreamResult(sw);
DOMSource source = new DOMSource(content);
TransformerFactory.newInstance().newTransformer().transform(source, result);
} catch (Exception e) {
e.printStackTrace();
}
return sw.toString();
}
@Override
public synchronized List<Node> executeNodeQuery(String xPath) throws P4IPException {
List<Node> resultList = new ArrayList<>();
try {
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(P4NamespaceContext.getInstance());
XPathExpression expr = xpath.compile(xPath);
NodeList nodes = (NodeList) expr.evaluate(content, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++)
resultList.add(nodes.item(i));
} catch (Exception e) {
throw new P4IPException("Unable to execute xPath " + xPath + " on IP " + id);
}
return resultList;
}
@Override
public synchronized List<String> executeQuery(String xPath) throws P4IPException {
List<String> resultList = new ArrayList<>();
for (Node result : this.executeNodeQuery(xPath))
resultList.add(result.getNodeValue());
return resultList;
}
@Override
public synchronized boolean hasAVMaterial() throws P4IPException {
Mets mets = this.getContentAsMets();
FileSec fileSec = mets.getFileSec();
if (fileSec == null) {
logger.debug("File Section not available...");
return false;
} else {
logger.debug("File Section found... validating.");
List<FileGrp> fileGrpList = fileSec.getFileGrp();
if (fileGrpList == null || fileGrpList.size() != 1) {
logger.debug("Unable to retrive FileGrp or wrong number of FileGrp");
throw new P4IPException("Unable to retrive FileGrp or wrong number of FileGrp");
}
List<FileType> fileTypeList = fileGrpList.get(0).getFile();
if (fileTypeList == null || fileTypeList.size() == 0) {
logger.debug("Unable to retrieve File List or no File found");
throw new P4IPException("Unable to retrieve File List or no File found");
}
// FIXME: only one file is allowed for each SIP
if (fileTypeList.size() != 1) {
logger.debug("More than one File found in the SIP...");
throw new P4IPException("More than one File found in the SIP...");
}
return true;
}
}
@Override
public synchronized boolean hasRights() throws P4IPException {
Mets mets = this.getContentAsMets();
List<AmdSecType> amdSecList = mets.getAmdSec();
int rightsCounter = 0;
for (AmdSecType amdSecType : amdSecList) {
List<MdSecType> rightsMdList = amdSecType.getRightsMD();
if (rightsMdList == null) {
continue;
} else {
for (MdSecType mdSecType : rightsMdList) {
if (mdSecType.getMdRef() != null && mdSecType.getMdRef().getHref() != null && !mdSecType.getMdRef().getHref().isEmpty()) {
rightsCounter++;
continue;
} else if (mdSecType.getMdWrap() != null && mdSecType.getMdWrap().getXmlData() != null) {
rightsCounter++;
continue;
} else {
throw new P4IPException("Invalid rightsMD section...");
}
}
}
}
switch (rightsCounter) {
case 0:
return false;
case 1:
return true;
default:
throw new P4IPException("Too many rightsMD sections...");
}
}
@Override
public synchronized List<String> getAVFormats() throws P4IPException {
return this.executeQuery("//mets:file/@MIMETYPE");
}
@Override
public synchronized List<String> getAVMaterial(String mimeType, String location) throws P4IPException {
List<String> fLocats;
if (location.equals("URL"))
fLocats = this.executeQuery("//mets:file[@MIMETYPE='" + mimeType + "']/mets:FLocat[@LOCTYPE='OTHER' and @OTHERLOCTYPE='FILE']/@xlink:href");
else
fLocats = this.executeQuery("//mets:file[@MIMETYPE='" + mimeType + "']/mets:FLocat[@LOCTYPE='" + location + "' or @LOCTYPE='OTHER' and @OTHERLOCTYPE='" + location + "']/@xlink:href");
List<String> finalFLocats = new ArrayList<>();
for (String fLocat : fLocats) {
String finalFLocat;
switch (location) {
case "FILE":
finalFLocat = fLocat.replaceAll(ConfigurationManager.getPropertyInstance().getProperty(P4Property.P4_PLACEHOLDER), ConfigurationManager.getPropertyInstance().getProperty(P4Property.P4_STORAGE_VOLUME));
break;
case "URL":
finalFLocat = fLocat.replaceAll(ConfigurationManager.getPropertyInstance().getProperty(P4Property.P4_PLACEHOLDER), ConfigurationManager.getPropertyInstance().getProperty(P4Property.P4_URL));
break;
default:
finalFLocat = fLocat;
break;
}
finalFLocats.add(finalFLocat);
}
return finalFLocats;
}
@Override
public synchronized List<String> getAVMaterial(String mimeType, String metsLocType, String outputLocType) throws P4IPException {
List<String> fLocats;
if (metsLocType.equals("URL"))
fLocats = this.executeQuery("//mets:file[@MIMETYPE='" + mimeType + "']/mets:FLocat[@LOCTYPE='URL']/@xlink:href");
else
fLocats = this.executeQuery("//mets:file[@MIMETYPE='" + mimeType + "']/mets:FLocat[@LOCTYPE='" + metsLocType + "' or @LOCTYPE='OTHER' and @OTHERLOCTYPE='" + metsLocType + "']/@xlink:href");
List<String> finalFLocats = new ArrayList<>();
for (String fLocat : fLocats) {
String finalFLocat;
switch (outputLocType) {
case "FILE":
finalFLocat = fLocat.replaceAll(ConfigurationManager.getPropertyInstance().getProperty(P4Property.P4_PLACEHOLDER), ConfigurationManager.getPropertyInstance().getProperty(P4Property.P4_STORAGE_VOLUME));
break;
case "URL":
finalFLocat = fLocat.replaceAll(ConfigurationManager.getPropertyInstance().getProperty(P4Property.P4_PLACEHOLDER), ConfigurationManager.getPropertyInstance().getProperty(P4Property.P4_URL));
break;
default:
finalFLocat = fLocat;
break;
}
finalFLocats.add(finalFLocat);
}
return finalFLocats;
}
@Override
public String getChecksum(String mimeType, String checksumType) throws P4IPException {
List<String> checksums = this.executeQuery("//mets:file[@MIMETYPE='" + mimeType + "' and @CHECKSUMTYPE='" + checksumType + "']/@CHECKSUM");
if (checksums.size() > 0)
return checksums.get(0);
else
return null;
}
@Override
public int getDuration() throws P4IPException {
return (int) (Double.parseDouble(this.executeQuery("//dnx:record/dnx:key[@id = 'duration']/text()").get(0)) * 1000);
}
@Override
public GregorianCalendar getCreateDate() throws P4IPException {
List<String> dates = this.executeQuery("//mets:metsHdr/@CREATEDATE");
if (dates != null && !dates.isEmpty()) {
String createDate = dates.get(0);
if (createDate != null && !createDate.isEmpty()) {
try {
return DatatypeFactory.newInstance().newXMLGregorianCalendar(createDate).toGregorianCalendar();
} catch (DatatypeConfigurationException e) {
throw new P4IPException("Unable to parse METS date...");
}
}
}
return null;
}
@Override
public Map<String, List<String>> getDCFields() throws P4IPException {
Map<String, List<String>> dcFields = new HashMap<>();
for (DCField field : DCField.values())
dcFields.put(field.toString(), this.executeQuery("//dc:record/dc:" + field + "/node()"));
return dcFields;
}
@Override
public synchronized List<String> getDCField(DCField field) throws P4IPException {
return this.getDCFields().get(field.toString());
}
public class P4Resource {
private P4Collection collection;
private String resourceID;
private Node resourceNode;
public P4Resource(P4Collection collection, String resourceID, Node resourceNode) {
this.collection = collection;
this.resourceID = resourceID;
this.resourceNode = resourceNode;
}
public P4Collection getCollection() {
return collection;
}
public String getResourceID() {
return resourceID;
}
public Node getResourceNode() {
return resourceNode;
}
}
}