/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE file at the root of the source
* tree and available online at
*
* https://github.com/keeps/roda
*/
package org.roda.core.common;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlValidationError;
import org.roda.core.data.exceptions.GenericException;
import org.roda.core.data.v2.validation.ValidationException;
import org.roda.core.data.v2.validation.ValidationIssue;
import org.roda.core.data.v2.validation.ValidationReport;
import org.roda.core.storage.ContentPayload;
import org.roda.core.storage.InputStreamContentPayload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is an utility class for metadata helpers.
*
* @author Luis Faria <lfaria@keep.pt>
*/
public final class MetadataUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(MetadataUtils.class);
/** Private empty constructor */
private MetadataUtils() {
}
/**
* Saves the current XML object to a byte array.
*
* @param xmlObject
*
* @return a <code>byte[]</code> with the contents of the XML file.
* @throws ValidationException
* @throws GenericException
*
* @throws MetadataException
* if the XML object is not valid or if something goes wrong with
* the serialisation.
*/
public static byte[] saveToByteArray(XmlObject xmlObject) throws GenericException, ValidationException {
return saveToByteArray(xmlObject, true);
}
/**
* Saves the current XML object to a byte array.
*
* @param xmlObject
* @param writeXMLDeclaration
*
* @return a <code>byte[]</code> with the contents of the XML file.
* @throws ValidationException
* @throws GenericException
*
*/
public static byte[] saveToByteArray(XmlObject xmlObject, boolean writeXMLDeclaration)
throws GenericException, ValidationException {
// Save the xml object to a byte array
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
saveToOutputStream(xmlObject, byteArrayStream, writeXMLDeclaration);
return byteArrayStream.toByteArray();
}
public static String saveToString(XmlObject xmlObject, boolean writeXMLDeclaration)
throws GenericException, ValidationException {
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
saveToOutputStream(xmlObject, byteArrayStream, writeXMLDeclaration);
return byteArrayStream.toString();
}
public static ContentPayload saveToContentPayload(final XmlObject xmlObject, final boolean writeXMLDeclaration) {
return new InputStreamContentPayload(new ProvidesInputStream() {
@Override
public InputStream createInputStream() throws IOException {
try {
return MetadataUtils.createInputStream(xmlObject, writeXMLDeclaration);
} catch (GenericException | ValidationException e) {
throw new IOException(e);
}
}
});
}
/**
* Saves the current XML object to a {@link File}.
*
* @param xmlObject
* the XML object to save.
* @param file
* the {@link File}.
*
* @throws MetadataException
* if the XML object is not valid or if something goes wrong with
* the serialisation.
*
* @throws FileNotFoundException
* if the specified {@link File} couldn't be opened.
* @throws IOException
* if {@link FileOutputStream} associated with the {@link File}
* couldn't be closed.
* @throws ValidationException
* @throws GenericException
*/
public static void saveToFile(XmlObject xmlObject, Path path)
throws IOException, GenericException, ValidationException {
OutputStream outputStream = Files.newOutputStream(path);
saveToOutputStream(xmlObject, outputStream, true);
outputStream.close();
}
/**
* Saves the current XML object to an {@link OutputStream}.
*
* @param xmlObject
* the XML object to save.
* @param outputStream
* the {@link OutputStream}.
* @param writeXMLDeclaration
*
* @throws GenericException
* @throws ValidationException
*/
public static void saveToOutputStream(XmlObject xmlObject, OutputStream outputStream, boolean writeXMLDeclaration)
throws GenericException, ValidationException {
LOGGER.trace("Serializing XML Object {}", xmlObject);
// Create an XmlOptions instance and set the error listener.
XmlOptions validateOptions = new XmlOptions();
List<XmlValidationError> errorList = new ArrayList<>();
validateOptions.setErrorListener(errorList);
// Validate the XML.
boolean isValid = xmlObject.validate(validateOptions);
if (isValid) {
try {
XmlOptions xmlSaveOptions = new XmlOptions().setUseDefaultNamespace().setSavePrettyPrint()
.setSaveAggressiveNamespaces();
if (!writeXMLDeclaration) {
xmlSaveOptions = xmlSaveOptions.setSaveNoXmlDecl();
}
xmlObject.save(outputStream, xmlSaveOptions);
} catch (IOException e) {
LOGGER.debug("Error serializing XML object - " + e.getMessage(), e);
throw new GenericException("Error serializing XML object", e);
}
} else {
throw new ValidationException(xmlValidationErrorsToValidationReport(errorList));
}
}
public static InputStream createInputStream(XmlObject xmlObject, boolean writeXMLDeclaration)
throws GenericException, ValidationException {
// Create an XmlOptions instance and set the error listener.
XmlOptions validateOptions = new XmlOptions();
List<XmlValidationError> errorList = new ArrayList<>();
validateOptions.setErrorListener(errorList);
// Validate the XML.
boolean isValid = xmlObject.validate(validateOptions);
if (isValid) {
XmlOptions xmlSaveOptions = new XmlOptions().setUseDefaultNamespace().setSavePrettyPrint()
.setSaveAggressiveNamespaces();
if (!writeXMLDeclaration) {
xmlSaveOptions = xmlSaveOptions.setSaveNoXmlDecl();
}
return xmlObject.newInputStream(xmlSaveOptions);
} else {
throw new ValidationException(xmlValidationErrorsToValidationReport(errorList));
}
}
public static ValidationReport xmlValidationErrorsToValidationReport(List<XmlValidationError> validationErrors) {
ValidationReport report = new ValidationReport();
report.setValid(false);
List<ValidationIssue> issues = new ArrayList<>();
for (XmlValidationError error : validationErrors) {
ValidationIssue issue = new ValidationIssue();
issue.setMessage(error.getMessage());
issue.setColumnNumber(error.getColumn());
issue.setLineNumber(error.getLine());
issues.add(issue);
}
report.setIssues(issues);
return report;
}
}