/*******************************************************************************
* Copyright 2006 - 2014 Vienna University of Technology,
* Department of Software Technology and Interactive Systems, IFS
*
* Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package eu.scape_project.planning.xml;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.codec.binary.Base64;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.scape_project.planning.exception.PlanningException;
import eu.scape_project.planning.model.Alternative;
import eu.scape_project.planning.model.ChangeLog;
import eu.scape_project.planning.model.DetailedExperimentInfo;
import eu.scape_project.planning.model.DigitalObject;
import eu.scape_project.planning.model.ExecutablePlanDefinition;
import eu.scape_project.planning.model.Experiment;
import eu.scape_project.planning.model.Parameter;
import eu.scape_project.planning.model.Plan;
import eu.scape_project.planning.model.PlanDefinition;
import eu.scape_project.planning.model.Policy;
import eu.scape_project.planning.model.PolicyNode;
import eu.scape_project.planning.model.SampleObject;
import eu.scape_project.planning.model.TargetValueObject;
import eu.scape_project.planning.model.Trigger;
import eu.scape_project.planning.model.measurement.Attribute;
import eu.scape_project.planning.model.measurement.CriterionCategory;
import eu.scape_project.planning.model.measurement.Measure;
import eu.scape_project.planning.model.measurement.Measurement;
import eu.scape_project.planning.model.scales.FreeStringScale;
import eu.scape_project.planning.model.scales.RestrictedScale;
import eu.scape_project.planning.model.scales.Scale;
import eu.scape_project.planning.model.scales.ScaleType;
import eu.scape_project.planning.model.transform.NumericTransformer;
import eu.scape_project.planning.model.transform.OrdinalTransformer;
import eu.scape_project.planning.model.tree.Leaf;
import eu.scape_project.planning.model.tree.TemplateTree;
import eu.scape_project.planning.model.tree.TreeNode;
import eu.scape_project.planning.model.util.FloatFormatter;
import eu.scape_project.planning.model.values.Value;
import eu.scape_project.planning.xml.plan.TimestampFormatter;
/**
* Static methods providing means to export projects to XML using dom4j.
*
* @author Michael Kraxner, Markus Hamm
*/
public class ProjectExporter implements Serializable {
private static final long serialVersionUID = 7538933914251415135L;
private static final Base64 encoder = new Base64(PlanXMLConstants.BASE64_LINE_LENGTH, PlanXMLConstants.BASE64_LINE_BREAK);
private Logger log = LoggerFactory.getLogger(ProjectExporter.class);;
private TimestampFormatter formatter = new TimestampFormatter();
private FloatFormatter floatFormatter = new FloatFormatter();
public static OutputFormat prettyFormat = new OutputFormat(" ", true, PlanXMLConstants.ENCODING); // OutputFormat.createPrettyPrint();
public static OutputFormat compactFormat = new OutputFormat(null, false, PlanXMLConstants.ENCODING); // OutputFormat.createPrettyPrint();
private static final Namespace XSI_NAMESPACE = new Namespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
private static final Namespace PLATO_NAMESPACE = new Namespace("", PlanXMLConstants.PLATO_NS);
public ProjectExporter() {
}
// /**
// * Returns the xml-representation of the given project as a String. NOTE: It
// * writes all data - including encoded binary data - directly to the DOM
// * tree this may result in performance problems for large amounts of data.
// *
// * @param p
// * the plan to export
// * @return the XML representation of the plan
// * @throws PlanningException
// * if an error occured during export
// */
// public String exportToString(Plan p) throws PlanningException {
// return exportToXml(p).asXML();
// }
/**
* Takes the given project and turns it into a dom4j-xml-representation.
*
* NOTE: It does NOT write the binary data, but only the ids instead !
*
* @param p
* the plan to export
* @return dom4j-document representing the given project
* @throws PlanningException
* if an error occured during export
*/
public Document exportToXml(Plan p) throws PlanningException {
Document doc = createProjectDoc();
addProject(p, doc, false);
return doc;
}
/**
* Creates a dom4j document template to add plans.
*
* @return the dom4j document
*/
public Document createProjectDoc() {
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement("plans");
root.add(XSI_NAMESPACE);
root.add(PLATO_NAMESPACE);
root.addAttribute(XSI_NAMESPACE.getPrefix() + ":schemaLocation", PlanXMLConstants.PLATO_NS + " "
+ PlanXMLConstants.PLATO_SCHEMA);
root.add(new Namespace("fits", "http://hul.harvard.edu/ois/xml/ns/fits/fits_output"));
// set version of corresponding schema
root.addAttribute("version", PlanXMLConstants.PLATO_SCHEMA_VERSION);
return doc;
}
/**
* Creates a template document.
*
* @return the dom4j-document
*/
public Document createTemplateDoc() {
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement("templates");
root.add(XSI_NAMESPACE);
return doc;
}
/**
* This method creates a template xml from the objective tree contained in
* Plan p. The template xml is of the form
*
* <templates> <template name="Public Fragments"> <node name="Template 1"
* weight="0.0" single="false" lock="false"> <node
* name="Interactive multimedia presentations" weight="0.0" single="false"
* lock="false"> ... </template> </templates>
*
* For creating the tree we use
* {@link ProjectExporter#addSubTree(TreeNode, Element)} and remove
* afterwards the evaluation part which we don't want to have in the
* template.
*
* @param p
* preservation plan
* @param templateLibrary
* name of the template library (e.g. 'Public Templates')
* @param name
* name of the template
* @param description
* description of the template
* @return xml as string
*/
public String getObjectiveTreeAsTemplate(Plan p, String templateLibrary, String name, String description) {
Document templateDoc = createTemplateDoc();
Element template = templateDoc.getRootElement().addElement("template");
template.addAttribute("name", templateLibrary);
// Objectivetree (including weights, evaluation values and transformers)
if (p.getTree().getRoot() != null) {
addSubTree(p.getTree().getRoot(), template);
}
// get the root node of the tree
Element treeRootNode = (Element) template.selectSingleNode("//templates/template/node");
// change the name of the template
if (name == null) {
name = "";
}
treeRootNode.addAttribute("name", name);
if (description == null) {
description = "";
}
// set the description of the template
Element descriptionElement = treeRootNode.addElement("description");
descriptionElement.setText(description);
// remove the evaluation from the template
@SuppressWarnings("unchecked")
List<Element> nodes = templateDoc.selectNodes("//leaf/evaluation");
for (Element n : nodes) {
n.getParent().remove(n);
}
return templateDoc.asXML();
}
/**
* Takes the given projects and turns them into a dom4j-xml-representation.
* NOTE: It writes all data - including encoded binary data - directly to
* the DOM tree this may result in performance problems for large amounts of
* data.
*
* @return dom4j-document representing the given project public static
* Document exportToXml(List<Plan> projects) { Document doc =
* createProjectDoc();
*
* for (Plan project : projects) {
* ProjectExporter.addProject(project, doc, null, null); } return
* doc; }
*/
/**
* Writes the xml-representation of the given projects to the given target
* file. NOTE: It writes all data - including encoded binary data - directly
* to the DOM tree this may result in performance problems for large amounts
* of data.
*
* @param p
* the plan to export
* @param target
* the file to write the plan
* @throws IOException
* if an error occured during write
* @throws PlanningException
* if an error occured during export
*/
public void exportToFile(Plan p, File target) throws IOException, PlanningException {
XMLWriter writer = new XMLWriter(new FileWriter(target), ProjectExporter.prettyFormat);
try {
writer.write(exportToXml(p));
} finally {
writer.close();
}
}
// /**
// * Writes the xml-representation of the given project into a temporary file
// * and returns the java-representation of this file. NOTE: It writes all
// * data - including encoded binary data - directly to the DOM tree this may
// * result in performance problems for large amounts of data.
// *
// * @param p
// * the plan to export
// * @throws IOException
// * if an error occured during export
// * @throws PlanningException
// * if an error occured during export
// *
// */
// public File exportToFile(Plan p) throws IOException, PlanningException {
// File temp = File.createTempFile("plato-plan-export-", ".xml");
// exportToFile(p, temp);
// return temp;
// }
/*
* public static File exportTemplatesToFile(List<TemplateTree> trees) throws
* IOException { File temp = File.createTempFile("plato-templates-export-",
* ".xml"); XMLWriter writer = new XMLWriter(new FileWriter(temp),
* ProjectExporter.prettyFormat); //writer.setMaximumAllowedCharacter(127);
* writer.write(ProjectExporter.exportTemplates(trees)); writer.close();
* return temp; }
*/
public Document exportTemplates(List<TemplateTree> trees) {
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement("templates");
for (TemplateTree template : trees) {
addTemplateTree(template, root);
}
return doc;
}
/**
* Adds the XML representation of the complete template tree to the parent
* node <code>root</code>.
*
* @param tree
* @param root
*/
private void addTemplateTree(TemplateTree tree, Element root) {
Element template = root.addElement("template");
template.addAttribute("name", tree.getName());
template.addAttribute("owner", tree.getOwner());
for (TreeNode child : tree.getRoot().getChildren()) {
addSubTree(child, template);
}
}
private void addSubPolicyTree(PolicyNode data, Element xmlRoot) {
if (data == null) {
return;
}
if (data.isPolicy()) {
Element policy = xmlRoot.addElement("policy");
Policy p = (Policy) data;
policy.addAttribute("name", p.getName());
policy.addAttribute("value", p.getValue());
} else {
Element policyNode = xmlRoot.addElement("policyNode");
policyNode.addAttribute("name", data.getName());
for (PolicyNode pn : data.getChildren()) {
addSubPolicyTree(pn, policyNode);
}
}
}
/**
* Adds the XML-representation of the treenode <code>data</code> to the
* parent node <code>xmlRoot</code>. (also called recursivly for its
* children)
*
* @param data
* @param xmlRoot
*/
private void addSubTree(TreeNode data, Element xmlRoot) {
if (data.isLeaf()) {
Element leaf = xmlRoot.addElement("leaf");
addNodeAttributes(data, leaf);
Leaf l = (Leaf) data;
leaf.addElement("aggregationMode").addAttribute("value", l.getAggregationMode().name());
String typename = null;
/*
* Scale: A special element is created, depending on the type of the
* scale
*/
Scale s = l.getScale();
if (s != null) {
typename = deriveElementname(s.getClass());
addScale(s, leaf);
// Transformer
if (l.getTransformer() != null) {
Element transformer = leaf.addElement(deriveElementname(l.getTransformer().getClass()));
if (l.getTransformer() instanceof OrdinalTransformer) {
Map<String, TargetValueObject> mapping = ((OrdinalTransformer) l.getTransformer()).getMapping();
Element mappings = transformer.addElement("mappings");
for (String ordinal : mapping.keySet()) {
mappings
.addElement("mapping")
.addAttribute("ordinal", ordinal)
.addAttribute("target",
floatFormatter.formatFloatPrecisly(mapping.get(ordinal).getValue()));
}
}
if (l.getTransformer() instanceof NumericTransformer) {
NumericTransformer nt = (NumericTransformer) l.getTransformer();
transformer.addElement("mode").addAttribute("value", nt.getMode().name());
Element thresholds = transformer.addElement("thresholds");
thresholds.addElement("threshold1").setText(
floatFormatter.formatFloatPrecisly(nt.getThreshold1()));
thresholds.addElement("threshold2").setText(
floatFormatter.formatFloatPrecisly(nt.getThreshold2()));
thresholds.addElement("threshold3").setText(
floatFormatter.formatFloatPrecisly(nt.getThreshold3()));
thresholds.addElement("threshold4").setText(
floatFormatter.formatFloatPrecisly(nt.getThreshold4()));
thresholds.addElement("threshold5").setText(
floatFormatter.formatFloatPrecisly(nt.getThreshold5()));
}
addChangeLog(l.getTransformer().getChangeLog(), transformer);
}
if (l.isMapped()) {
addMeasure(l.getMeasure(), leaf);
}
Element eval = leaf.addElement("evaluation");
typename = typename.substring(0, typename.lastIndexOf("Scale"));
/*
* keep in mind: there are only values of the considered
* alternatives in the map
*/
for (String a : l.getValueMap().keySet()) {
Element alt = eval.addElement("alternative");
alt.addAttribute("key", a);
addStringElement(alt, "comment", l.getValueMap().get(a).getComment());
for (Value v : l.getValueMap().get(a).getList()) {
/*
* A special element is created, depending on the type
* of the scale
*/
Element valElement = alt.addElement(typename + "Result");
String valueStr = v.toString();
if (!"".equals(valueStr)) {
addStringElement(valElement, "value", v.toString());
}
addStringElement(valElement, "comment", v.getComment());
addChangeLog(v.getChangeLog(), valElement);
}
}
}
} else { // not a leaf
Element node = xmlRoot.addElement("node");
addNodeAttributes(data, node);
for (TreeNode child : data.getChildren()) {
addSubTree(child, node);
}
}
}
/**
* creates a new element with all information of the given measure and adds
* it to the parent node.
*
* @param info
* @param parent
*/
private void addMeasure(Measure measure, Element parent) {
Element measureEl = parent.addElement("measure");
measureEl.addAttribute("ID", measure.getUri());
addStringElement(measureEl, "name", measure.getName());
addStringElement(measureEl, "description", measure.getDescription());
Attribute attribute = (Attribute) measure.getAttribute();
if (attribute != null) {
Element attributeEl = measureEl.addElement("attribute");
attributeEl.addAttribute("ID", attribute.getUri());
addStringElement(attributeEl, "name", attribute.getName());
addStringElement(attributeEl, "description", attribute.getDescription());
CriterionCategory category = attribute.getCategory();
Element categoryEl = attributeEl.addElement("category");
categoryEl.addAttribute("ID", category.getUri());
categoryEl.addAttribute("scope", category.getScope().toString());
addStringElement(categoryEl, "name", category.getName());
}
addScale(measure.getScale(), measureEl);
// addChangeLog(measure.getChangeLog(), measureEl);
}
/**
* Adds the given node's properties to the xmlNode.
*/
private void addNodeAttributes(TreeNode data, Element xmlNode) {
xmlNode.addAttribute("name", data.getName()).addAttribute("weight",
floatFormatter.formatFloatPrecisly(data.getWeight()));
if (data instanceof Leaf) {
xmlNode.addAttribute("single", Boolean.toString(data.isSingle()));
}
xmlNode.addAttribute("lock", Boolean.toString(data.isLock()));
addStringElement(xmlNode, "description", data.getDescription());
addChangeLog(data.getChangeLog(), xmlNode);
}
/**
* Adds a changelog element to the provided parent if changelog is defined.
*
* @param log
* the changelog to add
* @param parent
* the parent element
* @return the newly created element or null
*/
private Element addChangeLog(ChangeLog log, Element parent) {
Element xmlNode = null;
if (log != null) {
xmlNode = parent.addElement("changelog")
.addAttribute("created", formatter.formatTimestamp(log.getCreated()))
.addAttribute("createdBy", log.getCreatedBy())
.addAttribute("changed", formatter.formatTimestamp(log.getChanged()))
.addAttribute("changedBy", log.getChangedBy());
}
return xmlNode;
}
/**
* Adds the digital object to the parentNode. If the object is null or has
* no data, it is not added.
*
* @param upload
* the DigitalObject to add
* @param parent
* the parent element of the element to create
* @param elementName
* the name of the element to create
* @param addDigitalObjectData
* true if the data should be written, false otherwise
* @return the newly created element or null if none was created
* @throws PlanningException
* if an error occured during export
*/
private Element addUpload(DigitalObject upload, Element parent, String elementName, boolean addDigitalObjectData) throws PlanningException {
Element xmlNode = null;
if (upload != null && upload.isDataExistent()) {
xmlNode = addEncodedDigitalObject(upload, parent, elementName, addDigitalObjectData);
}
return xmlNode;
}
/**
* Adds the digital object to the parentNode. If the object is null it is
* not added.
*
* @param upload
* the DigitalObject to add
* @param parent
* the parent element of the element to create
* @param elementName
* the name of the element to create
* @param addDigitalObjectData
* true if the data should be written, false otherwise
* @return the newly created element or null if none was created
* @throws PlanningException
* if an error occured during export
*/
private Element addEncodedDigitalObject(DigitalObject upload, Element parent, String elementName, boolean addDigitalObjectData) throws PlanningException {
Element xmlNode = null;
if (upload != null) {
xmlNode = parent.addElement(elementName);
xmlNode.addAttribute("fullname", upload.getFullname()).addAttribute("contentType", upload.getContentType());
Element data = xmlNode.addElement("data");
if (upload.isDataExistent()) {
data.addAttribute("hasData", "true");
data.addAttribute("encoding", "base64");
if (!addDigitalObjectData) {
// Add only DigitalObject ID, it can be replaced later
data.setText(String.valueOf(upload.getId()));
} else {
// Add encoded data
data.setText(encodeBase64(upload.getData().getData()));
}
} else {
data.addAttribute("hasData", "false");
}
addUpload(upload.getXcdlDescription(), xmlNode, "xcdlDescription", addDigitalObjectData);
addJhoveInfo(upload, xmlNode);
addFitsInfo(upload, xmlNode);
Element formatInfo = xmlNode.addElement("formatInfo")
.addAttribute("puid", upload.getFormatInfo().getPuid())
.addAttribute("name", upload.getFormatInfo().getName())
.addAttribute("version", upload.getFormatInfo().getVersion())
.addAttribute("mimeType", upload.getFormatInfo().getMimeType())
.addAttribute("defaultExtension", upload.getFormatInfo().getDefaultExtension());
addChangeLog(upload.getFormatInfo().getChangeLog(), formatInfo);
addChangeLog(upload.getChangeLog(), xmlNode);
}
return xmlNode;
}
/**
* Adds the Jhove information of the digital object to the provided element
* if it has one.
*
* @param digitalObject
* the digital object
* @param parent
* the parent element of the element to create
* @return the newly created element or null if none was created
* @throws PlanningException
* if an error occured during export
*/
private Element addJhoveInfo(DigitalObject digitalObject, Element parent)
throws PlanningException {
Element jhoveElement = null;
String jhoveXML = digitalObject.getJhoveXMLString();
if ((jhoveXML != null) && (!"".equals(jhoveXML))) {
jhoveElement = parent.addElement("jhoveXML");
jhoveElement.addAttribute("encoding", "base64");
try {
jhoveElement.setText(encodeBase64(jhoveXML.getBytes(PlanXMLConstants.ENCODING)));
} catch (UnsupportedEncodingException e) {
log.error("Error writing JHOVE info {}.", e.getMessage());
throw new PlanningException("Error writing JHOVE info.", e);
}
}
return jhoveElement;
}
/**
* Adds the fits information of the digital object to the provided element
* if it has one.
*
* @param digitalObject
* the digital object
* @param parent
* the parent element of the element to create
* @return the newly created element or null if none was created
* @throws PlanningException
* if an error occured during export
*/
private Element addFitsInfo(DigitalObject digitalObject, Element parent)
throws PlanningException {
Element fitsElement = null;
String fitsInfo = digitalObject.getFitsXMLString();
if ((fitsInfo != null) && (!"".equals(fitsInfo))) {
fitsElement = parent.addElement("fitsXML");
fitsElement.addAttribute("encoding", "base64");
try {
fitsElement.setText(encodeBase64(fitsInfo.getBytes(PlanXMLConstants.ENCODING)));
} catch (UnsupportedEncodingException e) {
log.error("Error writing fits info {}.", e.getMessage());
throw new PlanningException("Error writing fits info.", e);
}
}
return fitsElement;
}
/**
* Creates an element name from the provided class.
*
* @param c
* the class to use
* @return an element name
*/
private String deriveElementname(Class<?> c) {
String name = c.getName();
name = name.substring(name.lastIndexOf(".") + 1);
name = name.substring(0, 1).toLowerCase() + name.substring(1);
return name;
}
/**
* Adds the XML-representation of the given project to the parent
* <code>projectNode</code>.
*
* @param p
* the plan to add
* @param projectsDoc
* the document where the plan should be added
* @param addDigitalObjectData
* whether the digital object data should be added to the XML
* @throws PlanningException
* if an error occured during export
*/
public void addProject(Plan p, Document projectsDoc, boolean addDigitalObjectData) throws PlanningException {
Element projectNode = projectsDoc.getRootElement().addElement(new QName("plan", PLATO_NAMESPACE));
addChangeLog(p.getChangeLog(), projectNode);
Element properties = projectNode.addElement("properties");
addUpload(p.getPlanProperties().getReportUpload(), properties, "report", addDigitalObjectData);
// Plan state
properties.addElement("state").addAttribute("value",
Integer.toString(p.getPlanProperties().getState().getValue()));
// Plan properties
properties.addAttribute("author", p.getPlanProperties().getAuthor())
.addAttribute("organization", p.getPlanProperties().getOrganization())
.addAttribute("name", p.getPlanProperties().getName())
.addAttribute("privateProject", Boolean.toString(p.getPlanProperties().isPrivateProject()))
.addAttribute("reportPublic", Boolean.toString(p.getPlanProperties().isReportPublic()))
.addAttribute("repositoryIdentifier", p.getPlanProperties().getRepositoryIdentifier())
.addAttribute("playground", Boolean.toString(p.getPlanProperties().isPlayground()));
addStringElement(properties, "description", p.getPlanProperties().getDescription());
addStringElement(properties, "owner", p.getPlanProperties().getOwner());
addChangeLog(p.getPlanProperties().getChangeLog(), properties);
// Plan Basis
Element basis = projectNode.addElement("basis");
basis.addAttribute("identificationCode", p.getProjectBasis().getIdentificationCode());
addStringElement(basis, "documentTypes", p.getProjectBasis().getDocumentTypes());
addStringElement(basis, "applyingPolicies", p.getProjectBasis().getApplyingPolicies());
addStringElement(basis, "designatedCommunity", p.getProjectBasis().getDesignatedCommunity());
addStringElement(basis, "mandate", p.getProjectBasis().getMandate());
addStringElement(basis, "organisationalProcedures", p.getProjectBasis().getOrganisationalProcedures());
addStringElement(basis, "planningPurpose", p.getProjectBasis().getPlanningPurpose());
addStringElement(basis, "planRelations", p.getProjectBasis().getPlanRelations());
addStringElement(basis, "preservationRights", p.getProjectBasis().getPreservationRights());
addStringElement(basis, "referenceToAgreements", p.getProjectBasis().getReferenceToAgreements());
Element triggers = basis.addElement("triggers");
if (p.getProjectBasis().getTriggers() != null) {
addTrigger(p.getProjectBasis().getTriggers().getNewCollection(), triggers);
addTrigger(p.getProjectBasis().getTriggers().getPeriodicReview(), triggers);
addTrigger(p.getProjectBasis().getTriggers().getChangedEnvironment(), triggers);
addTrigger(p.getProjectBasis().getTriggers().getChangedObjective(), triggers);
addTrigger(p.getProjectBasis().getTriggers().getChangedCollectionProfile(), triggers);
}
Element policyTree = basis.addElement("policyTree");
addSubPolicyTree(p.getProjectBasis().getPolicyTree().getRoot(), policyTree);
addChangeLog(p.getProjectBasis().getChangeLog(), basis);
// Sample Records
Element samplerecords = projectNode.addElement("sampleRecords");
addStringElement(samplerecords, "samplesDescription", p.getSampleRecordsDefinition().getSamplesDescription());
Element collectionProfile = samplerecords.addElement("collectionProfile");
if (p.getSampleRecordsDefinition().getCollectionProfile() != null) {
addStringElement(collectionProfile, "collectionID", p.getSampleRecordsDefinition().getCollectionProfile()
.getCollectionID());
addStringElement(collectionProfile, "description", p.getSampleRecordsDefinition().getCollectionProfile()
.getDescription());
addStringElement(collectionProfile, "expectedGrowthRate", p.getSampleRecordsDefinition()
.getCollectionProfile().getExpectedGrowthRate());
addStringElement(collectionProfile, "numberOfObjects", p.getSampleRecordsDefinition()
.getCollectionProfile().getNumberOfObjects());
addStringElement(collectionProfile, "typeOfObjects", p.getSampleRecordsDefinition().getCollectionProfile()
.getTypeOfObjects());
addStringElement(collectionProfile, "retentionPeriod", p.getSampleRecordsDefinition()
.getCollectionProfile().getRetentionPeriod());
addUpload(p.getSampleRecordsDefinition().getCollectionProfile().getProfile(), collectionProfile, "profile",
addDigitalObjectData);
}
for (SampleObject rec : p.getSampleRecordsDefinition().getRecords()) {
Element sampleRecord = addEncodedDigitalObject(rec, samplerecords, "record", addDigitalObjectData);
if (sampleRecord != null) {
sampleRecord.addAttribute("shortName", rec.getShortName());
addStringElement(sampleRecord, "description", rec.getDescription());
addStringElement(sampleRecord, "originalTechnicalEnvironment", rec.getOriginalTechnicalEnvironment());
}
}
addChangeLog(p.getSampleRecordsDefinition().getChangeLog(), samplerecords);
// Requirementsdefinition
Element rdef = projectNode.addElement("requirementsDefinition");
addStringElement(rdef, "description", p.getRequirementsDefinition().getDescription());
Element uploads = rdef.addElement("uploads");
for (DigitalObject upload : p.getRequirementsDefinition().getUploads()) {
addUpload(upload, uploads, "upload", addDigitalObjectData);
}
addChangeLog(p.getRequirementsDefinition().getChangeLog(), rdef);
// Alternatives
Element alternatives = projectNode.addElement("alternatives");
addStringElement(alternatives, "description", p.getAlternativesDefinition().getDescription());
for (Alternative a : p.getAlternativesDefinition().getAlternatives()) {
/*
* Export all alternatives (also discarded ones) Indices of the
* result-set reference only the considered alternatives!
*/
Element alt = alternatives.addElement("alternative")
.addAttribute("discarded", Boolean.toString(a.isDiscarded())).addAttribute("name", a.getName());
addStringElement(alt, "description", a.getDescription());
if (a.getAction() != null) {
Element action = alt.addElement("action");
action.addAttribute("shortname", a.getAction().getShortname())
.addAttribute("url", a.getAction().getUrl())
.addAttribute("actionIdentifier", a.getAction().getActionIdentifier())
.addAttribute("info", a.getAction().getInfo())
.addAttribute("targetFormat", a.getAction().getTargetFormat())
.addAttribute("executable", String.valueOf(a.getAction().isExecutable()));
addStringElement(action, "descriptor", a.getAction().getDescriptor());
addStringElement(action, "parameterInfo", a.getAction().getParameterInfo());
Element params = action.addElement("params");
if (a.getAction().getParams() != null) {
for (Parameter param : a.getAction().getParams()) {
params.addElement("param").addAttribute("name", param.getName())
.addAttribute("value", param.getValue());
}
}
addChangeLog(a.getAction().getChangeLog(), action);
}
Element resourceDescr = alt.addElement("resourceDescription");
addStringElement(resourceDescr, "necessaryResources", a.getResourceDescription().getNecessaryResources());
addStringElement(resourceDescr, "configSettings", a.getResourceDescription().getConfigSettings());
addStringElement(resourceDescr, "reasonForConsidering", a.getResourceDescription()
.getReasonForConsidering());
addChangeLog(a.getResourceDescription().getChangeLog(), resourceDescr);
Element experiment = alt.addElement("experiment");
Experiment exp = a.getExperiment();
addStringElement(experiment, "description", exp.getDescription());
addStringElement(experiment, "settings", exp.getSettings());
addUpload(exp.getWorkflow(), experiment, "workflow", addDigitalObjectData);
Element results = experiment.addElement("results");
for (Entry<SampleObject, DigitalObject> entry : exp.getResults().entrySet()) {
Element result = addUpload(entry.getValue(), results, "result", addDigitalObjectData);
if (result != null) {
result.addAttribute("key", entry.getKey().getShortName());
}
}
// // */experiment/xcdlDescriptions/xcdlDescription
// Element xcdls = experiment.addElement("xcdlDescriptions");
// for (SampleObject record : exp.getResults().keySet()) {
// DigitalObject result = exp.getResults().get(record);
// if (result != null) {
// XcdlDescription x = result.getXcdlDescription();
// if (x != null) {
// // only existing xcdls are exported
// Element upload = addUpload(x, xcdls, "xcdlDescription",
// encoder, uploadIDs);
// if (upload != null) {
// upload.addAttribute("key", record.getShortName());
// }
// }
// }
// }
// export detailed experiment info's
Element detailedInfos = experiment.addElement("detailedInfos");
for (SampleObject record : exp.getDetailedInfo().keySet()) {
DetailedExperimentInfo dinfo = exp.getDetailedInfo().get(record);
Element detailedInfo = detailedInfos.addElement("detailedInfo")
.addAttribute("key", record.getShortName()).addAttribute("successful", "" + dinfo.getSuccessful());
addStringElement(detailedInfo, "programOutput", dinfo.getProgramOutput());
addStringElement(detailedInfo, "cpr", dinfo.getCpr());
Element measurements = detailedInfo.addElement("measurements");
for (Measurement m : dinfo.getMeasurements().values()) {
Element measurement = measurements.addElement("measurement");
measurement.addAttribute("measureId", m.getMeasureId());
// measurement.value:
String typename = deriveElementname(m.getValue().getClass());
Element valueElem = measurement.addElement(typename);
// .addAttribute("value", m.getValue().toString());
addStringElement(valueElem, "value", m.getValue().toString());
addChangeLog(m.getValue().getChangeLog(), valueElem);
}
}
addChangeLog(a.getExperiment().getChangeLog(), experiment);
addChangeLog(a.getChangeLog(), alt);
}
addChangeLog(p.getAlternativesDefinition().getChangeLog(), alternatives);
// go-nogo - is created in the go-nogo step and need not exist
if (p.getDecision() != null) {
Element decision = projectNode.addElement("decision");
addStringElement(decision, "reason", p.getDecision().getReason());
addStringElement(decision, "actionNeeded", p.getDecision().getActionNeeded());
decision.addElement("goDecision").addAttribute("value", p.getDecision().getDecision().name());
addChangeLog(p.getDecision().getChangeLog(), decision);
}
// Evaluation
Element evaluation = projectNode.addElement("evaluation");
addStringElement(evaluation, "comment", p.getEvaluation().getComment());
addChangeLog(p.getEvaluation().getChangeLog(), evaluation);
// importance weighting
Element importanceWeighting = projectNode.addElement("importanceWeighting");
addStringElement(importanceWeighting, "comment", p.getImportanceWeighting().getComment());
addChangeLog(p.getImportanceWeighting().getChangeLog(), importanceWeighting);
// Recommendation
Element recommendation = projectNode.addElement("recommendation");
if (p.getRecommendation().getAlternative() != null) {
recommendation.addAttribute("alternativeName", p.getRecommendation().getAlternative().getName());
}
addStringElement(recommendation, "reasoning", p.getRecommendation().getReasoning());
addStringElement(recommendation, "effects", p.getRecommendation().getEffects());
addChangeLog(p.getRecommendation().getChangeLog(), recommendation);
// transformation
Element trafo = projectNode.addElement("transformation");
addStringElement(trafo, "comment", p.getTransformation().getComment());
addChangeLog(p.getTransformation().getChangeLog(), trafo);
// Objectivetree (including weights, evaluation values and
// transformers)
Element tree = projectNode.addElement("tree");
tree.addAttribute("weightsInitialized", "" + p.getTree().isWeightsInitialized());
if (p.getTree().getRoot() != null) {
addSubTree(p.getTree().getRoot(), tree);
}
// add ExecutablePlanDefinition
Element executablePlanDef = projectNode.addElement("executablePlan");
ExecutablePlanDefinition executablePlanDefinition = p.getExecutablePlanDefinition();
addStringElement(executablePlanDef, "objectPath", executablePlanDefinition.getObjectPath());
addStringElement(executablePlanDef, "toolParameters", executablePlanDefinition.getToolParameters());
addStringElement(executablePlanDef, "triggersConditions", executablePlanDefinition.getTriggersConditions());
addStringElement(executablePlanDef, "validateQA", executablePlanDefinition.getValidateQA());
addUpload(executablePlanDefinition.getT2flowExecutablePlan(), executablePlanDef, "workflow", addDigitalObjectData);
addChangeLog(executablePlanDefinition.getChangeLog(), executablePlanDef);
// Export generated preservation action plan
addPreservationActionPlan(p.getPreservationActionPlan(), projectNode, addDigitalObjectData);
// Plan definition
Element planDef = projectNode.addElement("planDefinition");
PlanDefinition pdef = p.getPlanDefinition();
planDef.addAttribute("currency", pdef.getCurrency());
addStringElement(planDef, "costsIG", pdef.getCostsIG());
addStringElement(planDef, "costsPA", pdef.getCostsPA());
addStringElement(planDef, "costsPE", pdef.getCostsPE());
addStringElement(planDef, "costsQA", pdef.getCostsQA());
addStringElement(planDef, "costsREI", pdef.getCostsREI());
addStringElement(planDef, "costsRemarks", pdef.getCostsRemarks());
addStringElement(planDef, "costsRM", pdef.getCostsRM());
addStringElement(planDef, "costsTCO", pdef.getCostsTCO());
addStringElement(planDef, "responsibleExecution", pdef.getResponsibleExecution());
addStringElement(planDef, "responsibleMonitoring", pdef.getResponsibleMonitoring());
triggers = planDef.addElement("triggers");
if (pdef.getTriggers() != null) {
addTrigger(pdef.getTriggers().getNewCollection(), triggers);
addTrigger(pdef.getTriggers().getPeriodicReview(), triggers);
addTrigger(pdef.getTriggers().getChangedEnvironment(), triggers);
addTrigger(pdef.getTriggers().getChangedObjective(), triggers);
addTrigger(pdef.getTriggers().getChangedCollectionProfile(), triggers);
}
addChangeLog(pdef.getChangeLog(), planDef);
}
/**
* Adds the provided trigger to the parent element.
*
* @param t
* the trigger to add
* @param parent
* the parent of the element to create
* @return the newly created element or null if none was created
*/
private Element addTrigger(Trigger t, Element parent) {
Element trigger = null;
if (t != null) {
trigger = parent.addElement("trigger");
trigger.addAttribute("type", t.getType().name());
trigger.addAttribute("active", Boolean.toString(t.isActive()));
trigger.addAttribute("description", t.getDescription());
}
return trigger;
}
/**
* Long strings are stored as XML-elements, not as attributes. It is not
* possible to add an element with value <code>null</code>, therefore this
* has to be handled here: A new element is only added if there is a value
* at all.
*
* @param parent
* the parent element of the element to create
* @param name
* the name of the element to create
* @param value
* the value of the element to create
* @return the newly created element
*/
private Element addStringElement(Element parent, String name, String value) {
Element e = null;
// &&(!"".equals(value)
if (value != null) {
e = parent.addElement(name);
if (!"".equals(value)) {
e.addText(value);
}
}
return e;
}
public String exportTreeToFreemind(Plan plan) {
return exportTreeToFreemind(plan.getTree().getRoot());
}
public String exportTreeToFreemind(TreeNode treeRoot) {
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement("map");
Namespace xsi = new Namespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
root.add(xsi);
root.addAttribute("version", "0.8.1");
root.addComment("To view this file, download free mind mapping software FreeMind from http://freemind.sourceforge.net");
addSubTreeFreemind(root, treeRoot);
String xml = doc.asXML();
// PlatoLogger.getLogger(ProjectExporter.class).debug(arg0)
return xml;
}
private void addSubTreeFreemind(Element xmlElement, TreeNode node) {
Element element = xmlElement.addElement("node");
addFreemindAttributes(node, element);
if (node.isLeaf()) {
// add scale
Leaf leaf = (Leaf) node;
Scale scale = leaf.getScale();
if (scale != null) {
Element scaleElement = element.addElement("node");
String restriction = "?";
// restrictions: restrictedscale, ordinals -freestring
if (scale instanceof FreeStringScale) {
restriction = "free text";
} else if ((scale.getType() == ScaleType.ordinal || scale.getType() == ScaleType.restricted)
&& !"".equals(((RestrictedScale) scale).getRestriction())) {
restriction = ((RestrictedScale) scale).getRestriction();
} else {
restriction = scale.getUnit();
}
scaleElement.addAttribute("TEXT", restriction);
}
} else {
// add children
for (TreeNode child : node.getChildren()) {
addSubTreeFreemind(element, child);
}
}
}
private void addFreemindAttributes(TreeNode node, Element element) {
element.addAttribute("TEXT", node.getName());
// TODO export weights? units? single? >> future.
String mInfoUri = null;
if (node.isLeaf()) {
Leaf leaf = (Leaf) node;
if (leaf.isMapped()) {
mInfoUri = leaf.getMeasure().getUri();
}
}
// add DESCRIPTION if existent
if (((mInfoUri != null) && (!"".equals(mInfoUri)))
|| (node.getDescription() != null && !"".equals(node.getDescription()))) {
Element hook = element.addElement("hook");
hook.addAttribute("NAME", "accessories/plugins/NodeNote.properties");
Element description = hook.addElement("text");
String descr = "";
// and measurement info
if ((mInfoUri != null) && (!"".equals(mInfoUri))) {
descr = "measureId=" + mInfoUri + "\n";
}
if (node.getDescription() != null) {
descr = descr + node.getDescription();
}
description.setText(descr);
}
}
/**
* Adds the provided scale to the parent element.
*
* @param s
* the scale to add
* @param parent
* the parent element of the element to add
* @return the newly created element
*/
private Element addScale(Scale s, Element parent) {
Element scale = null;
if (s != null) {
String typename = deriveElementname(s.getClass());
scale = parent.addElement(typename);
// && (!"".equals(s.getUnit()
if (s.getUnit() != null) {
scale.addAttribute("unit", s.getUnit());
}
// scale.addAttribute("displayName", s.getDisplayName());
if (s instanceof RestrictedScale) {
scale.addAttribute("restriction", ((RestrictedScale) s).getRestriction());
}
addChangeLog(s.getChangeLog(), scale);
}
return scale;
}
/**
* Adds a preservation action plan element to the provided parent if the
* preservation action plan is defined.
*
* @param preservationActionPlan
* the preservation action plan to add
* @param parent
* the parent element
* @param addDigitalObjectData
* true if the data should be written, false otherwise
* @return the newly created element or null
* @throws PlanningException
* if an error occurred during creation
*/
private Element addPreservationActionPlan(DigitalObject preservationActionPlan, Element parent,
boolean addDigitalObjectData) throws PlanningException {
Element preservationActionPlanElement = null;
if (preservationActionPlan != null) {
if (preservationActionPlan != null && preservationActionPlan.isDataExistent()) {
if (!addDigitalObjectData) {
preservationActionPlanElement = parent.addElement("preservationActionPlan");
preservationActionPlanElement.setText(String.valueOf(preservationActionPlan.getId()));
} else {
Document doc;
try {
doc = DocumentHelper
.parseText(new String(preservationActionPlan.getData().getData(), PlanXMLConstants.ENCODING));
if (doc.getRootElement().hasContent()) {
preservationActionPlanElement = doc.getRootElement();
parent.add(preservationActionPlanElement);
}
} catch (UnsupportedEncodingException e) {
log.error("Error parsing preservation action plan {}.", e.getMessage());
throw new PlanningException("Error parsing preservation action plan.", e);
} catch (DocumentException e) {
log.error("Error parsing preservation action plan {}.", e.getMessage());
throw new PlanningException("Error parsing preservation action plan.", e);
}
}
}
}
return preservationActionPlanElement;
}
private String encodeBase64(byte[] data) {
return encoder.encodeAsString(data);
}
}