/**
*
* Copyright (c) 2009-2016 Freedomotic team http://freedomotic.com
*
* This file is part of Freedomotic
*
* 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 2, 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
* Freedomotic; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
package com.freedomotic.persistence;
import com.freedomotic.exceptions.DataUpgradeException;
import com.freedomotic.model.environment.Environment;
import com.freedomotic.model.object.EnvObject;
import com.freedomotic.reactions.Command;
import com.freedomotic.reactions.Reaction;
import com.freedomotic.reactions.Trigger;
import com.freedomotic.settings.Info;
import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Uses XSLT (http://en.wikipedia.org/wiki/XSLT) to transform and update XML
* files. Transformation file should be in conf/validator folder and have a
* naming schema like /conf/validator/TYPE-upgrade-FROMVERSION.xslt
* /conf/validator/things-upgrade-5.5.0.xslt"
*
* @author Matteo Mazzoni
*/
class DataUpgradeServiceImpl implements DataUpgradeService<String> {
private static final Logger LOG = LoggerFactory.getLogger(DataUpgradeServiceImpl.class.getCanonicalName());
// Cache the loaded transformations
private Map<File, Source> sources = new HashMap<>();
/**
* {@inheritDoc}
*/
@Override
public String upgrade(Class type, String xml, String fromVersion) throws DataUpgradeException {
if (fromVersion.trim().equals(Info.getVersion())) {
LOG.debug("Given data are already consistent with the most recent framework version. No XML transformation was performed");
return xml;
} else {
LOG.info("Upgrading data of type \"{}\" from version {} to version {}",
new Object[]{type.getCanonicalName(), fromVersion, Info.getVersion()});
}
String upgradedXml;
Source xsltAlgorthm;
try {
if (type == EnvObject.class) {
xsltAlgorthm = getTransformationAlgorithm("thing", fromVersion);
} else if (type == Environment.class) {
xsltAlgorthm = getTransformationAlgorithm("environment", fromVersion);
} else if (type == Reaction.class) {
xsltAlgorthm = getTransformationAlgorithm("reaction", fromVersion);
} else if (type == Command.class) {
xsltAlgorthm = getTransformationAlgorithm("command", fromVersion);
} else if (type == Trigger.class) {
xsltAlgorthm = getTransformationAlgorithm("trigger", fromVersion);
} else {
// Return an exception if it's not a class that this service able to upgrade
throw new DataUpgradeException("Data upgrade service: upgrading entities of type " + type.getCanonicalName() + " is not supported");
}
upgradedXml = upgradeContent(xml, xsltAlgorthm);
} catch (TransformerException | DataUpgradeException transformerException) {
throw new DataUpgradeException("Error while upgrading an XML data source", transformerException);
}
return upgradedXml;
}
/**
* Upgrades a xml content to the current framework version
*
* @param input
* @param xsltAlgorithm
* @return
* @throws TransformerConfigurationException
* @throws TransformerException
*/
private String upgradeContent(String input, Source xsltAlgorithm) throws TransformerConfigurationException, TransformerException {
// Load all the needed resources
StreamSource streamSource = new StreamSource(new StringReader(input));
StreamResult streamResult = new StreamResult(new StringWriter());
// Create the transformer
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(xsltAlgorithm);
// Apply the transformation algorithm defined in the XSLT file
transformer.transform(streamSource, streamResult);
return streamResult.getWriter().toString();
}
/**
* Loads the right XSLT transformation script according to the version of
* the data to transform.
*
* @param fromVersion The original data version which should be made
* compatible with the current framework version
* @return the transformation Source
*/
private Source getTransformationAlgorithm(String baseFile, String fromVersion) {
// Take the source from cache or load it from file
File xsltFile = new File(Info.PATHS.PATH_CONFIG_FOLDER + "/validator/" + baseFile + "-upgrade-" + fromVersion + ".xslt");
if (!sources.containsKey(xsltFile)) {
Source source = new StreamSource(xsltFile);
sources.put(xsltFile, source);
}
Source result = sources.get(xsltFile);
if (result == null) {
throw new IllegalStateException("Cannot load a valid XSLT transformation file from " + xsltFile.getAbsolutePath());
}
return sources.get(xsltFile);
}
}