/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* 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
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package ddf.catalog.services.xsltlistener;
import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.io.IOUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.helpers.XMLReaderFactory;
import ddf.catalog.Constants;
import ddf.catalog.data.BinaryContent;
import ddf.catalog.data.Metacard;
import ddf.catalog.transform.CatalogTransformerException;
import ddf.catalog.transform.MetacardTransformer;
public class XsltMetacardTransformer extends AbstractXsltTransformer
implements MetacardTransformer {
private static final Logger LOGGER = LoggerFactory.getLogger(XsltMetacardTransformer.class);
private Map<String, Object> localMap = new ConcurrentHashMap<String, Object>();
public XsltMetacardTransformer() {
}
public XsltMetacardTransformer(Bundle bundle, String xslFile) {
super(bundle, xslFile);
}
private String getValueOrEmptyString(Object value) {
return (value == null) ? "" : value.toString();
}
@Override
public BinaryContent transform(Metacard metacard, Map<String, Serializable> arguments)
throws CatalogTransformerException {
LOGGER.debug("Entering metacard xslt transform.");
Transformer transformer;
Map<String, Object> mergedMap = new HashMap<String, Object>(localMap);
if (arguments != null) {
mergedMap.putAll(arguments);
}
// adding metacard data not in document
mergedMap.put("id", getValueOrEmptyString(metacard.getId()));
mergedMap.put("siteName", getValueOrEmptyString(metacard.getSourceId()));
mergedMap.put("title", getValueOrEmptyString(metacard.getTitle()));
mergedMap.put("type", getValueOrEmptyString(metacard.getMetacardType()));
mergedMap.put("date", getValueOrEmptyString(metacard.getCreatedDate()));
mergedMap.put("product", getValueOrEmptyString(metacard.getResourceURI()));
mergedMap.put("thumbnail", getValueOrEmptyString(metacard.getThumbnail()));
mergedMap.put("geometry", getValueOrEmptyString(metacard.getLocation()));
ServiceReference[] refs = null;
try {
LOGGER.debug("Searching for other Metacard Transformers.");
// TODO INJECT THESE!!!
refs = context.getServiceReferences(MetacardTransformer.class.getName(), null);
} catch (InvalidSyntaxException e) {
// can't happen because filter is null
}
if (refs != null) {
List<String> serviceList = new ArrayList<String>();
LOGGER.debug(
"Found other Metacard transformers, adding them to a service reference list.");
for (ServiceReference ref : refs) {
if (ref != null) {
String title = null;
String shortName = (String) ref.getProperty(Constants.SERVICE_SHORTNAME);
if ((title = (String) ref.getProperty(Constants.SERVICE_TITLE)) == null) {
title = "View as " + shortName.toUpperCase();
}
String url =
"/services/catalog/" + metacard.getId() + "?transform=" + shortName;
// define the services
serviceList.add(title);
serviceList.add(url);
}
}
mergedMap.put("services", serviceList);
} else {
LOGGER.debug("No other Metacard transformers were found.");
}
// TODO: maybe add updated, type, and uuid here?
// map.put("updated", fmt.print(result.getPostedDate().getTime()));
// map.put("type", card.getSingleType().getValue());
BinaryContent resultContent;
StreamResult resultOutput = null;
XMLReader xmlReader = null;
try {
XMLReader xmlParser = XMLReaderFactory.createXMLReader();
xmlParser.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlParser.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
xmlParser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",
false);
xmlReader = new XMLFilterImpl(xmlParser);
} catch (SAXException e) {
LOGGER.debug(e.getMessage(), e);
}
Source source = new SAXSource(xmlReader,
new InputSource(new StringReader(metacard.getMetadata())));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
resultOutput = new StreamResult(baos);
try {
transformer = templates.newTransformer();
} catch (TransformerConfigurationException tce) {
throw new CatalogTransformerException(
"Could not perform Xslt transform: " + tce.getException(), tce.getCause());
}
if (!mergedMap.isEmpty()) {
for (Map.Entry<String, Object> entry : mergedMap.entrySet()) {
LOGGER.debug("Adding parameter to transform {}:{}",
entry.getKey(),
entry.getValue());
transformer.setParameter(entry.getKey(), entry.getValue());
}
}
try {
transformer.transform(source, resultOutput);
byte[] bytes = baos.toByteArray();
IOUtils.closeQuietly(baos);
LOGGER.debug("Transform complete.");
resultContent = new XsltTransformedContent(bytes, mimeType);
} catch (TransformerException te) {
throw new CatalogTransformerException(
"Could not perform Xslt transform: " + te.getMessage(), te.getCause());
} finally {
// TODO: if we ever start to reuse transformers, we should add this
// code back in
// transformer.reset();
}
return resultContent;
}
/**
* Gets the dataItemStatus value.
*
* @return String representation of the dataItemStatus value (defaults to "Unknown")
*/
public String getDataItemStatus() {
return localMap.get("dataItemStatus")
.toString();
}
/**
* Sets the dataItemStatus value that is passed to the translation and put in the dataItemStatus
* element.
*
* @param dataItemStatus
*/
public void setDataItemStatus(String dataItemStatus) {
localMap.put("dataItemStatus", dataItemStatus);
}
}