/**
* Copyright (c) Codice Foundation
*
* 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.
*
* 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 org.codice.ddf.spatial.ogc.csw.catalog.converter;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.startsWith;
import static net.opengis.cat.csw.v_2_0_2.ElementSetType.BRIEF;
import static net.opengis.cat.csw.v_2_0_2.ElementSetType.FULL;
import static net.opengis.cat.csw.v_2_0_2.ElementSetType.SUMMARY;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.IOUtils;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswConstants;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswRecordMetacardType;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.exceptions.XpathException;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.DataHolder;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.core.TreeMarshaller;
import com.thoughtworks.xstream.core.TreeUnmarshaller;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.xml.DomReader;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.WstxDriver;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.thoughtworks.xstream.io.xml.XppReader;
import ddf.catalog.data.AttributeType.AttributeFormat;
import ddf.catalog.data.BinaryContent;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.impl.MetacardImpl;
import ddf.catalog.transform.CatalogTransformerException;
import net.opengis.cat.csw.v_2_0_2.ElementSetType;
public class TestCswRecordConverter {
private static final transient Logger LOGGER = LoggerFactory
.getLogger(TestCswRecordConverter.class);
private static final String THUMBNAIL_URL = "THUMBNAIL_URL";
private static final DatatypeFactory XSD_FACTORY;
private static final GregorianCalendar CREATED_DATE = new GregorianCalendar(2014, 10, 1);
private static final GregorianCalendar MODIFIED_DATE = new GregorianCalendar(2016, 10, 1);
private static final GregorianCalendar EFFECTIVE_DATE = new GregorianCalendar(2015, 10, 1);
private static final String ACTION_URL = "http://example.com/source/id?transform=resource";
private static DateTimeFormatter dateFormatter;
private static String modified;
private static String effective;
private static String created;
private static CswRecordConverter converter;
static {
DatatypeFactory factory = null;
try {
factory = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException e) {
LOGGER.error("Failed to create xsdFactory: {}", e.getMessage());
}
XSD_FACTORY = factory;
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
dateFormatter = ISODateTimeFormat.dateOptionalTimeParser();
modified = XSD_FACTORY.newXMLGregorianCalendar(MODIFIED_DATE).toXMLFormat();
effective = XSD_FACTORY.newXMLGregorianCalendar(EFFECTIVE_DATE).toXMLFormat();
created = XSD_FACTORY.newXMLGregorianCalendar(CREATED_DATE).toXMLFormat();
converter = new CswRecordConverter();
}
@Test
public void testUnmarshalSingleCswRecordToMetacard() {
XStream xstream = new XStream(new WstxDriver());
xstream.registerConverter(converter);
xstream.alias("Record", MetacardImpl.class);
InputStream is = TestCswRecordConverter.class.getResourceAsStream("/Csw_Record_Text.xml");
Metacard mc = (Metacard) xstream.fromXML(is);
assertThat(mc, not(nullValue()));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_IDENTIFIER).getValue(),
startsWith("urn:uuid:e933"));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_SUBJECT).getValue(),
equalTo("Land titles"));
Date returned = (Date) mc.getAttribute(CswRecordMetacardType.CSW_DATE).getValue();
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.clear();
cal.set(2003, 4, 9);
Date expected = new Date(cal.getTimeInMillis());
assertThat(returned, equalTo(expected));
assertThat(mc.getResourceURI(), is(nullValue()));
}
@Test
public void testUnmarshalNoNamespaceDeclaration() throws IOException, SAXException {
XStream xstream = new XStream(new XppDriver());
xstream.registerConverter(converter);
xstream.alias("Record", MetacardImpl.class);
xstream.alias("csw:Record", MetacardImpl.class);
InputStream is = IOUtils.toInputStream(getRecordNoNamespaceDeclaration());
Metacard mc = (Metacard) xstream.fromXML(is);
Metacard expectedMetacard = getTestMetacard();
assertThat(mc, not(nullValue()));
assertThat(mc.getContentTypeName(), equalTo(expectedMetacard.getContentTypeName()));
assertThat(mc.getCreatedDate(), equalTo(expectedMetacard.getCreatedDate()));
assertThat(mc.getEffectiveDate(), equalTo(expectedMetacard.getEffectiveDate()));
assertThat(mc.getId(), equalTo(expectedMetacard.getId()));
assertThat(mc.getModifiedDate(), equalTo(expectedMetacard.getModifiedDate()));
assertThat(mc.getTitle(), equalTo(expectedMetacard.getTitle()));
assertThat(mc.getResourceURI(), equalTo(expectedMetacard.getResourceURI()));
}
@Test
public void testUnmarshalWriteNamespaces() throws IOException, SAXException,
XmlPullParserException {
XStream xstream = new XStream(new XppDriver());
xstream.registerConverter(converter);
xstream.alias("Record", MetacardImpl.class);
xstream.alias("csw:Record", MetacardImpl.class);
InputStream is = IOUtils.toInputStream(getRecordNoNamespaceDeclaration());
HierarchicalStreamReader reader = new XppReader(new InputStreamReader(is),
XmlPullParserFactory.newInstance().newPullParser());
DataHolder args = xstream.newDataHolder();
Map<String, String> namespaces = new HashMap<>();
namespaces.put(CswConstants.XMLNS + CswConstants.NAMESPACE_DELIMITER
+ CswConstants.CSW_NAMESPACE_PREFIX, CswConstants.CSW_OUTPUT_SCHEMA);
namespaces.put(CswConstants.XMLNS + CswConstants.NAMESPACE_DELIMITER
+ CswConstants.DUBLIN_CORE_NAMESPACE_PREFIX, CswConstants.DUBLIN_CORE_SCHEMA);
namespaces.put(CswConstants.XMLNS + CswConstants.NAMESPACE_DELIMITER
+ CswConstants.DUBLIN_CORE_TERMS_NAMESPACE_PREFIX,
CswConstants.DUBLIN_CORE_TERMS_SCHEMA);
namespaces.put(CswConstants.XMLNS + CswConstants.NAMESPACE_DELIMITER
+ CswConstants.OWS_NAMESPACE_PREFIX, CswConstants.OWS_NAMESPACE);
args.put(CswConstants.WRITE_NAMESPACES, namespaces);
Metacard mc = (Metacard) xstream.unmarshal(reader, null, args);
Metacard expectedMetacard = getTestMetacard();
assertThat(mc, not(nullValue()));
assertThat(mc.getContentTypeName(), equalTo(expectedMetacard.getContentTypeName()));
assertThat(mc.getCreatedDate(), equalTo(expectedMetacard.getCreatedDate()));
assertThat(mc.getEffectiveDate(), equalTo(expectedMetacard.getEffectiveDate()));
assertThat(mc.getId(), equalTo(expectedMetacard.getId()));
assertThat(mc.getModifiedDate(), equalTo(expectedMetacard.getModifiedDate()));
assertThat(mc.getTitle(), equalTo(expectedMetacard.getTitle()));
assertThat(mc.getResourceURI(), equalTo(expectedMetacard.getResourceURI()));
XMLUnit.setIgnoreWhitespace(true);
assertXMLEqual(mc.getMetadata(), getControlRecord());
}
@Test
public void testUnmarshalSingleCswRecordToMetacardContentTypeMapsToFormat() throws
ParserConfigurationException, IOException, SAXException {
XStream xstream = new XStream(new WstxDriver());
Map<String, String> metacardAttributeMappings = getMetacardAttributeMappings();
metacardAttributeMappings.put(CswRecordMetacardType.CSW_FORMAT, Metacard.CONTENT_TYPE);
xstream.registerConverter(converter);
xstream.alias("csw:Record", MetacardImpl.class);
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse("src/test/resources/Csw_Record.xml");
HierarchicalStreamReader reader = new DomReader(doc);
DataHolder holder = xstream.newDataHolder();
holder.put(CswConstants.CSW_MAPPING, metacardAttributeMappings);
Metacard mc = (Metacard) xstream.unmarshal(reader, null, holder);
assertThat(mc, not(nullValue()));
assertThat(mc.getContentTypeName(), is("PDF"));
}
@Test
public void testUnmarshalCswRecordGeometryToMetacard() {
XStream xstream = new XStream(new WstxDriver());
xstream.registerConverter(converter);
xstream.alias("Record", MetacardImpl.class);
InputStream is = TestCswRecordConverter.class
.getResourceAsStream("/Csw_Record_with_Geometry.xml");
Metacard mc = (Metacard) xstream.fromXML(is);
assertThat(mc, not(nullValue()));
assertThat(mc.getLocation(), not(nullValue()));
}
/**
* CSW Record has multiple elements of same name and it is an element that had to be uniquely
* qualified between CSW and basic Metacard, e.g., "title" vs. "csw.title"
*/
@Test
public void testUnmarshalCswRecordMultipleTitles() {
XStream xstream = new XStream(new WstxDriver());
xstream.registerConverter(converter);
xstream.alias("Record", MetacardImpl.class);
InputStream is = TestCswRecordConverter.class
.getResourceAsStream("/Csw_Record_MultiValueFields.xml");
Metacard mc = (Metacard) xstream.fromXML(is);
assertThat(mc, not(nullValue()));
LOGGER.debug("Metacard title = {}", (String) mc.getAttribute(Metacard.TITLE).getValue());
LOGGER.debug("CSW title = {}",
(String) mc.getAttribute(CswRecordMetacardType.CSW_TITLE).getValue());
assertThat(mc.getTitle(), equalTo("First title"));
assertListStringAttribute(mc, CswRecordMetacardType.CSW_TITLE,
new String[] {"First title", "Second title"});
assertListStringAttribute(mc, CswRecordMetacardType.CSW_SUBJECT,
new String[] {"Subject 1", "Subject 2"});
}
@Test
public void testUnmarshalCswRecordMultipleResourceUri() {
XStream xstream = new XStream(new WstxDriver());
xstream.registerConverter(converter);
xstream.alias("Record", MetacardImpl.class);
InputStream is = TestCswRecordConverter.class
.getResourceAsStream("/Csw_Record_MultiValueFields.xml");
Metacard mc = (Metacard) xstream.fromXML(is);
assertThat(mc.getResourceURI().toString(), equalTo("http://example.com/product.pdf"));
}
// This test exercises all of the attributes shared between a basic metacard
// and
// the CSW metacard type, e.g., title, created/modified/effective dates.
@Test
public void testUnmarshalCswRecordMetacardAttributeOverlap() {
XStream xstream = new XStream(new WstxDriver());
xstream.registerConverter(converter);
xstream.alias("Record", MetacardImpl.class);
InputStream is = TestCswRecordConverter.class.getResourceAsStream("/Csw_Record.xml");
Metacard mc = (Metacard) xstream.fromXML(is);
assertThat(mc, not(nullValue()));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_IDENTIFIER).getValue(),
startsWith("08976079-9c53-465f-b921-97d0717262f5"));
assertThat((String) mc.getAttribute(Metacard.ID).getValue(),
equalTo((String) mc.getAttribute(CswRecordMetacardType.CSW_IDENTIFIER).getValue()));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_TYPE).getValue(),
equalTo("IMAGE-PRODUCT"));
// Verify extensible CSW attributes in metacard were populated
// created
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_CREATED).getValue(),
equalTo("2003-01-28T07:09:16Z"));
assertDates(Metacard.CREATED, CswRecordMetacardType.CSW_CREATED, mc);
// effective
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_DATE_ACCEPTED).getValue(),
equalTo("2013-07-12T16:16:16Z"));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_DATE_COPYRIGHTED).getValue(),
equalTo("2013-07-12T16:16:16Z"));
assertDates(Metacard.EFFECTIVE, CswRecordMetacardType.CSW_DATE_ACCEPTED, mc);
// modified
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_MODIFIED).getValue(),
equalTo("2013-05-15T19:15:15Z"));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_DATE_SUBMITTED).getValue(),
equalTo("2003-05-14T19:15:15Z"));
assertDates(Metacard.MODIFIED, CswRecordMetacardType.CSW_MODIFIED, mc);
}
@Test
public void testUnmarshalCswRecordMetacardNoModifiedDate() {
Metacard mc = buildMetacardFromCSW("/Csw_Record_without_ModifiedDate.xml");
assertThat(mc, not(nullValue()));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_IDENTIFIER).getValue(),
startsWith("08976079-9c53-465f-d921-97d0717262f5"));
assertThat((String) mc.getAttribute(Metacard.ID).getValue(),
equalTo((String) mc.getAttribute(CswRecordMetacardType.CSW_IDENTIFIER).getValue()));
// Verify extensible CSW attributes in metacard were populated
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_CREATED).getValue(),
equalTo("2003-01-28T07:09:16Z"));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_DATE_SUBMITTED).getValue(),
equalTo("2003-05-14T19:15:15Z"));
assertDates(Metacard.CREATED, CswRecordMetacardType.CSW_CREATED, mc);
assertDates(Metacard.MODIFIED, CswRecordMetacardType.CSW_DATE_SUBMITTED, mc);
}
@Test
public void testUnmarshalCswRecordMetacardNoDateSubmitted() {
Metacard mc = buildMetacardFromCSW("/Csw_Record_without_DateSubmitted.xml");
assertThat(mc, not(nullValue()));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_IDENTIFIER).getValue(),
startsWith("08976079-9c53-465f-c921-97d0717262f5"));
assertThat((String) mc.getAttribute(Metacard.ID).getValue(),
equalTo((String) mc.getAttribute(CswRecordMetacardType.CSW_IDENTIFIER).getValue()));
// Verify extensible CSW attributes in metacard were populated
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_CREATED).getValue(),
equalTo("2003-01-28T07:09:16Z"));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_MODIFIED).getValue(),
equalTo("2013-05-15T19:15:15Z"));
assertDates(Metacard.CREATED, CswRecordMetacardType.CSW_CREATED, mc);
assertDates(Metacard.MODIFIED, CswRecordMetacardType.CSW_MODIFIED, mc);
}
@Test
public void testUnmarshalCswRecordMetacardDateMappings() throws XmlPullParserException {
XStream xstream = new XStream(new WstxDriver());
xstream.registerConverter(converter);
xstream.alias("csw:Record", MetacardImpl.class);
InputStream is = TestCswRecordConverter.class.getResourceAsStream("/Csw_Record.xml");
HierarchicalStreamReader reader = new XppReader(new InputStreamReader(is),
XmlPullParserFactory.newInstance().newPullParser());
DataHolder args = xstream.newDataHolder();
Map<String, String> dateMappings = new HashMap<>();
dateMappings.put(CswRecordMetacardType.CSW_DATE_SUBMITTED, Metacard.EFFECTIVE);
dateMappings.put(CswRecordMetacardType.CSW_CREATED, Metacard.CREATED);
dateMappings.put(CswRecordMetacardType.CSW_MODIFIED, Metacard.MODIFIED);
dateMappings.put(CswRecordMetacardType.CSW_TYPE, Metacard.CONTENT_TYPE);
args.put(CswConstants.CSW_MAPPING, dateMappings);
Metacard mc = (Metacard) xstream.unmarshal(reader, null, args);
assertThat(mc, not(nullValue()));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_IDENTIFIER).getValue(),
is(equalTo("08976079-9c53-465f-b921-97d0717262f5")));
assertThat((String) mc.getAttribute(Metacard.ID).getValue(),
equalTo((String) mc.getAttribute(CswRecordMetacardType.CSW_IDENTIFIER).getValue()));
// Verify extensible CSW attributes in metacard were populated
// created
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_CREATED).getValue(),
equalTo("2003-01-28T07:09:16Z"));
assertDates(Metacard.CREATED, CswRecordMetacardType.CSW_CREATED, mc);
// effective
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_DATE_ACCEPTED).getValue(),
equalTo("2013-07-12T16:16:16Z"));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_DATE_COPYRIGHTED).getValue(),
equalTo("2013-07-12T16:16:16Z"));
assertDates(Metacard.EFFECTIVE, CswRecordMetacardType.CSW_DATE_SUBMITTED, mc);
// modified
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_MODIFIED).getValue(),
equalTo("2013-05-15T19:15:15Z"));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_DATE_SUBMITTED).getValue(),
equalTo("2003-05-14T19:15:15Z"));
assertDates(Metacard.MODIFIED, CswRecordMetacardType.CSW_MODIFIED, mc);
}
private Metacard buildMetacardFromCSW(String cswFileName) {
XStream xstream = new XStream(new WstxDriver());
xstream.registerConverter(converter);
xstream.alias("Record", MetacardImpl.class);
InputStream is = TestCswRecordConverter.class.getResourceAsStream(cswFileName);
Metacard mc = (Metacard) xstream.fromXML(is);
return mc;
}
@Test
public void testUnmarshalCswRecordWithCustomDateMappings() throws ParserConfigurationException,
IOException, SAXException {
XStream xstream = new XStream(new WstxDriver());
// Custom date mappings
Map<String, String> metacardAttributeMappings = new HashMap<String, String>();
metacardAttributeMappings.put(CswRecordMetacardType.CSW_MODIFIED, Metacard.EFFECTIVE);
metacardAttributeMappings.put(CswRecordMetacardType.CSW_CREATED, Metacard.CREATED);
metacardAttributeMappings.put(CswRecordMetacardType.CSW_DATE_SUBMITTED, Metacard.MODIFIED);
xstream.registerConverter(converter);
xstream.alias("csw:Record", MetacardImpl.class);
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse("src/test/resources/Csw_Record.xml");
HierarchicalStreamReader reader = new DomReader(doc);
DataHolder holder = xstream.newDataHolder();
holder.put(CswConstants.CSW_MAPPING, metacardAttributeMappings);
Metacard mc = (Metacard) xstream.unmarshal(reader, null, holder);
assertThat(mc, not(nullValue()));
// Verify extensible CSW attributes in metacard were populated
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_CREATED).getValue(),
equalTo("2003-01-28T07:09:16Z"));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_DATE_SUBMITTED).getValue(),
equalTo("2003-05-14T19:15:15Z"));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_MODIFIED).getValue(),
equalTo("2013-05-15T19:15:15Z"));
assertDates(Metacard.EFFECTIVE, CswRecordMetacardType.CSW_MODIFIED, mc);
assertDates(Metacard.CREATED, CswRecordMetacardType.CSW_CREATED, mc);
assertDates(Metacard.MODIFIED, CswRecordMetacardType.CSW_DATE_SUBMITTED, mc);
}
@Test
public void testUnmarshalCswRecordWithProductAndThumbnail() throws URISyntaxException,
IOException, JAXBException, ParserConfigurationException, SAXException {
XStream xstream = new XStream(new WstxDriver());
xstream.registerConverter(converter);
InputStream is = TestCswRecordConverter.class.getResourceAsStream("/Csw_Record.xml");
// get the URL to the thumbnail image and stick it in the xml string
// this makes the test filesystem indepedent
URL thumbnail = TestCswRecordConverter.class.getResource("/ddf_globe.png");
String xml = null;
if (thumbnail != null) {
StringWriter writer = new StringWriter();
IOUtils.copy(is, writer);
xml = writer.toString();
xml = xml.replace(THUMBNAIL_URL, thumbnail.toString());
}
xstream.alias("csw:Record", MetacardImpl.class);
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse(IOUtils.toInputStream(xml));
HierarchicalStreamReader reader = new DomReader(doc);
DataHolder holder = xstream.newDataHolder();
holder.put(Metacard.RESOURCE_URI, CswRecordMetacardType.CSW_SOURCE);
holder.put(Metacard.THUMBNAIL, CswRecordMetacardType.CSW_REFERENCES);
Metacard mc = (Metacard) xstream.unmarshal(reader, null, holder);
assertThat(mc, not(nullValue()));
// Verify resource URI populated
String productUrl = "http://example.com/product.pdf";
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_SOURCE).getValue(),
equalTo(productUrl));
assertThat(mc.getResourceURI(), equalTo(new URI(productUrl)));
// Verify the thumbnail is populated
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_REFERENCES).getValue(),
equalTo(thumbnail.toString()));
assertThat(mc.getThumbnail(), equalTo(getThumbnailByteArray(thumbnail)));
}
/**
* Verifies that Zulu time zone is valid in ISO 8601 date.
*/
@Test
public void testConvertISODateMetacardAttribute() {
String dateStr = "2013-05-03T17:25:04Z";
Serializable ser = CswRecordConverter
.convertStringValueToMetacardValue(AttributeFormat.DATE, dateStr);
assertThat(ser, not(nullValue()));
assertThat(Date.class.isAssignableFrom(ser.getClass()), is(true));
Date date = (Date) ser;
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTime(date);
assertThat(cal.get(Calendar.MONTH), equalTo(Calendar.MAY));
assertThat(cal.get(Calendar.YEAR), equalTo(2013));
assertThat(cal.get(Calendar.DAY_OF_MONTH), equalTo(3));
}
/**
* Verifies that if metacard's date has an invalid timezone in the ISO 8601 format that the
* current time is returned.
*/
@Test
public void testConvertInvalidTimeZoneInDateMetacardAttribute() {
String dateStr = "2013-05-13T10:56:39EDT";
Serializable ser = CswRecordConverter
.convertStringValueToMetacardValue(AttributeFormat.DATE, dateStr);
assertDateConversion(ser, Calendar.getInstance());
}
/**
* Verifies that if the metacard's date is not in ISO 8601 format the current time is returned.
*/
@Test
public void testConvertInvalidDateMetacardAttribute() {
String dateStr = "26021000ZFEB11";
Serializable ser = CswRecordConverter
.convertStringValueToMetacardValue(AttributeFormat.DATE, dateStr);
assertDateConversion(ser, Calendar.getInstance());
}
/**
* Test to verify that a metacard's content type is set to the CSW Record's type field.
*/
@Test
public void testSetMetacardContentTypeToCswRecordType() throws ParserConfigurationException,
SAXException, IOException {
// Setup
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.parse("src/test/resources/Csw_Record.xml");
HierarchicalStreamReader reader = new DomReader(doc);
Map<String, String> metacardAttributeMappings = this.getMetacardAttributeMappings();
metacardAttributeMappings.put(CswRecordMetacardType.CSW_TYPE, Metacard.CONTENT_TYPE);
UnmarshallingContext context = new TreeUnmarshaller(null, null, null, null);
context.put(CswConstants.CSW_MAPPING, metacardAttributeMappings);
// Perform test
Metacard metacard = (Metacard) converter.unmarshal(reader, context);
// Verify
LOGGER.debug("metacard id: {}", metacard.getId());
LOGGER.debug("metacard content type: {}", metacard.getContentTypeName());
assertThat(metacard.getContentTypeName(), is("IMAGE-PRODUCT"));
}
@Test
public void testMarshalRecord() throws IOException, JAXBException, SAXException,
XpathException {
Metacard metacard = getTestMetacard();
StringWriter stringWriter = new StringWriter();
PrettyPrintWriter writer = new PrettyPrintWriter(stringWriter);
MarshallingContext context = new TreeMarshaller(writer, null, null);
converter.marshal(metacard, writer, context);
String xml = stringWriter.toString();
assertThat(xml, containsString(CswConstants.CSW_RECORD));
assertRecordXml(xml, metacard, FULL);
}
@Test
public void testMarshalBriefRecord() throws IOException, JAXBException, SAXException,
XpathException {
Metacard metacard = getTestMetacard();
StringWriter stringWriter = new StringWriter();
PrettyPrintWriter writer = new PrettyPrintWriter(stringWriter);
MarshallingContext context = new TreeMarshaller(writer, null, null);
context.put(CswConstants.ELEMENT_SET_TYPE, ElementSetType.BRIEF);
converter.marshal(metacard, writer, context);
String xml = stringWriter.toString();
assertThat(xml, containsString(CswConstants.CSW_BRIEF_RECORD));
assertRecordXml(xml, metacard, BRIEF);
}
@Test
public void testMarshalSummaryRecord() throws IOException, JAXBException, SAXException,
XpathException {
Metacard metacard = getTestMetacard();
StringWriter stringWriter = new StringWriter();
PrettyPrintWriter writer = new PrettyPrintWriter(stringWriter);
MarshallingContext context = new TreeMarshaller(writer, null, null);
context.put(CswConstants.ELEMENT_SET_TYPE, ElementSetType.SUMMARY);
converter.marshal(metacard, writer, context);
String xml = stringWriter.toString();
assertThat(xml, containsString(CswConstants.CSW_SUMMARY_RECORD));
assertRecordXml(xml, metacard, SUMMARY);
}
@Test
public void testMarshalRecordWithNamespaces() throws IOException, JAXBException, SAXException,
XpathException {
Metacard metacard = getTestMetacard();
StringWriter stringWriter = new StringWriter();
PrettyPrintWriter writer = new PrettyPrintWriter(stringWriter);
MarshallingContext context = new TreeMarshaller(writer, null, null);
context.put(CswConstants.WRITE_NAMESPACES, true);
converter.marshal(metacard, writer, context);
String xml = stringWriter.toString();
XMLUnit.setIgnoreWhitespace(true);
assertXMLEqual(getControlRecord(), xml);
}
@Test
public void testMetacardTransform() throws IOException, JAXBException, SAXException,
XpathException, CatalogTransformerException {
Metacard metacard = getTestMetacard();
Map<String, Serializable> args = new HashMap<>();
args.put(CswConstants.WRITE_NAMESPACES, true);
BinaryContent content = converter.transform(metacard, args);
String xml = IOUtils.toString(content.getInputStream());
assertThat(xml,
containsString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"));
XMLUnit.setIgnoreWhitespace(true);
assertXMLEqual(getControlRecord(), xml);
}
@Test
public void testMetacardTransformOmitXmlDeclaration() throws IOException, JAXBException,
SAXException, XpathException, CatalogTransformerException {
Metacard metacard = getTestMetacard();
Map<String, Serializable> args = new HashMap<>();
args.put(CswConstants.WRITE_NAMESPACES, true);
args.put(CswConstants.OMIT_XML_DECLARATION, true);
BinaryContent content = converter.transform(metacard, args);
String xml = IOUtils.toString(content.getInputStream());
assertThat(xml, not(containsString(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>")));
XMLUnit.setIgnoreWhitespace(true);
assertXMLEqual(getControlRecord(), xml);
}
@Test
public void testMetacardTransformOmitNamespaces() throws IOException, JAXBException,
SAXException, XpathException, CatalogTransformerException {
Metacard metacard = getTestMetacard();
Map<String, Serializable> args = new HashMap<>();
args.put(CswConstants.WRITE_NAMESPACES, false);
BinaryContent content = converter.transform(metacard, args);
String xml = IOUtils.toString(content.getInputStream());
assertThat(xml, containsString("<csw:Record>"));
}
@Test
public void testInputTransformWithNoNamespaceDeclaration() throws IOException,
CatalogTransformerException {
InputStream is = IOUtils.toInputStream(getRecordNoNamespaceDeclaration());
Metacard mc = converter.transform(is);
Metacard expectedMetacard = getTestMetacard();
assertThat(mc, not(nullValue()));
assertThat(mc.getContentTypeName(), equalTo(expectedMetacard.getContentTypeName()));
assertThat(mc.getCreatedDate(), equalTo(expectedMetacard.getCreatedDate()));
assertThat(mc.getEffectiveDate(), equalTo(expectedMetacard.getEffectiveDate()));
assertThat(mc.getId(), equalTo(expectedMetacard.getId()));
assertThat(mc.getModifiedDate(), equalTo(expectedMetacard.getModifiedDate()));
assertThat(mc.getTitle(), equalTo(expectedMetacard.getTitle()));
assertThat(mc.getResourceURI(), equalTo(expectedMetacard.getResourceURI()));
}
@Test
public void testInputTransform() throws IOException, CatalogTransformerException {
InputStream is = TestCswRecordConverter.class.getResourceAsStream("/Csw_Record.xml");
Metacard mc = converter.transform(is);
assertThat(mc, not(nullValue()));
assertThat((String) mc.getAttribute(CswRecordMetacardType.CSW_IDENTIFIER).getValue(),
startsWith("08976079-9c53-465f-b921-97d0717262f5"));
}
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private MetacardImpl getTestMetacard() {
MetacardImpl metacard = new MetacardImpl();
metacard.setContentTypeName("I have some content type");
metacard.setContentTypeVersion("1.0.0");
metacard.setCreatedDate(CREATED_DATE.getTime());
metacard.setEffectiveDate(EFFECTIVE_DATE.getTime());
metacard.setId("ID");
metacard.setLocation("POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))");
metacard.setMetadata("metadata a whole bunch of metadata");
metacard.setModifiedDate(MODIFIED_DATE.getTime());
metacard.setResourceSize("123TB");
metacard.setSourceId("sourceID");
metacard.setTitle("This is my title");
try {
metacard.setResourceURI(new URI(ACTION_URL));
} catch (URISyntaxException e) {
LOGGER.debug("URISyntaxException", e);
}
return metacard;
}
private void assertListStringAttribute(Metacard mc, String attrName, String[] expectedValues) {
List<?> values = (List<?>) mc.getAttribute(attrName).getValues();
assertThat(values, not(nullValue()));
assertThat(values.size(), equalTo(expectedValues.length));
List<String> valuesList = new ArrayList<String>();
valuesList.addAll((List<? extends String>) values);
LOGGER.debug("valuesList: {}", valuesList);
assertThat(valuesList, hasItems(expectedValues));
}
private void assertDates(String metacardAttribute, String cswAttribute, Metacard mc) {
Date date = (Date) mc.getAttribute(metacardAttribute).getValue();
String expectedDateStr = (String) mc.getAttribute(cswAttribute).getValue();
Date expectedDate = dateFormatter.parseDateTime(expectedDateStr).toDate();
assertThat(date.getTime(), equalTo(expectedDate.getTime()));
}
private void assertDateConversion(Serializable ser, Calendar expectedDate) {
assertThat(ser, not(nullValue()));
assertThat(Date.class.isAssignableFrom(ser.getClass()), is(true));
Date date = (Date) ser;
Calendar cal = Calendar.getInstance();
cal.setTime(date);
assertThat(cal.get(Calendar.MONTH), equalTo(expectedDate.get(Calendar.MONTH)));
assertThat(cal.get(Calendar.YEAR), equalTo(expectedDate.get(Calendar.YEAR)));
assertThat(cal.get(Calendar.DAY_OF_MONTH),
equalTo(expectedDate.get(Calendar.DAY_OF_MONTH)));
}
private Map<String, String> getMetacardAttributeMappings() {
Map<String, String> metacardAttributeMappings = new HashMap<String, String>();
metacardAttributeMappings.put(CswRecordMetacardType.CSW_CREATED, Metacard.EFFECTIVE);
metacardAttributeMappings.put(CswRecordMetacardType.CSW_DATE_SUBMITTED, Metacard.CREATED);
metacardAttributeMappings.put(CswRecordMetacardType.CSW_MODIFIED, Metacard.MODIFIED);
return metacardAttributeMappings;
}
private byte[] getThumbnailByteArray(URL url) throws IOException {
InputStream is = url.openStream();
byte[] thumbnail = IOUtils.toByteArray(is);
IOUtils.closeQuietly(is);
return thumbnail;
}
private String getControlRecord() {
return "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<csw:Record xmlns:csw=\"http://www.opengis.net/cat/csw/2.0.2\" "
+ "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
+ "xmlns:dct=\"http://purl.org/dc/terms/\" "
+ "xmlns:ows=\"http://www.opengis.net/ows\">\n" + " <dct:created>" + created
+ "</dct:created>\n" + " <dc:date>" + modified + "</dc:date>\n"
+ " <dct:modified>" + modified + "</dct:modified>\n" + " <dct:dateSubmitted>"
+ modified + "</dct:dateSubmitted>\n" + " <dct:issued>" + modified
+ "</dct:issued>\n" + " <dc:identifier>ID</dc:identifier>\n"
+ " <dct:bibliographicCitation>ID</dct:bibliographicCitation>\n" + " <dc:source>"
+ ACTION_URL + "</dc:source>\n" + " <dc:title>This is my title</dc:title>\n"
+ " <dct:alternative>This is my title</dct:alternative>\n"
+ " <dc:type>I have some content type</dc:type>\n" + " <dct:dateAccepted>"
+ effective + "</dct:dateAccepted>\n" + " <dct:dateCopyrighted>" + effective
+ "</dct:dateCopyrighted>\n" + " <dc:publisher>sourceID</dc:publisher>\n"
+ " <ows:BoundingBox crs=\"urn:x-ogc:def:crs:EPSG:6.11:4326\">\n"
+ " <ows:LowerCorner>10.0 10.0</ows:LowerCorner>\n"
+ " <ows:UpperCorner>40.0 40.0</ows:UpperCorner>\n" + " </ows:BoundingBox>\n"
+ "</csw:Record>\n";
}
private String getRecordNoNamespaceDeclaration() {
return "<csw:Record>\n" + " <dct:created>" + created + "</dct:created>\n" + " <dc:date>"
+ modified + "</dc:date>\n" + " <dct:modified>" + modified + "</dct:modified>\n"
+ " <dct:dateSubmitted>" + modified + "</dct:dateSubmitted>\n" + " <dct:issued>"
+ modified + "</dct:issued>\n" + " <dc:identifier>ID</dc:identifier>\n"
+ " <dct:bibliographicCitation>ID</dct:bibliographicCitation>\n" + " <dc:source>"
+ ACTION_URL + "</dc:source>\n" + " <dc:title>This is my title</dc:title>\n"
+ " <dct:alternative>This is my title</dct:alternative>\n"
+ " <dc:type>I have some content type</dc:type>\n" + " <dct:dateAccepted>"
+ effective + "</dct:dateAccepted>\n" + " <dct:dateCopyrighted>" + effective
+ "</dct:dateCopyrighted>\n" + " <dc:publisher>sourceID</dc:publisher>\n"
+ " <ows:BoundingBox crs=\"urn:x-ogc:def:crs:EPSG:6.11:4326\">\n"
+ " <ows:LowerCorner>10.0 10.0</ows:LowerCorner>\n"
+ " <ows:UpperCorner>40.0 40.0</ows:UpperCorner>\n" + " </ows:BoundingBox>\n"
+ "</csw:Record>\n";
}
private void assertRecordXml(String xml, Metacard metacard, ElementSetType elemntSetType) {
switch (elemntSetType) {
case FULL:
assertThat(xml, containsString("<dct:bibliographicCitation>" + metacard.getId()
+ "</dct:bibliographicCitation>"));
assertThat(xml, containsString(
"<dct:alternative>" + metacard.getTitle() + "</dct:alternative>"));
assertThat(xml, containsString("<dc:date>" + modified + "</dc:date>"));
assertThat(xml, containsString("<dct:modified>" + modified + "</dct:modified>"));
assertThat(xml, containsString("<dct:created>" + created + "</dct:created>"));
assertThat(xml,
containsString("<dct:dateAccepted>" + effective + "</dct:dateAccepted>"));
assertThat(xml,
containsString("<dct:dateCopyrighted>" + effective + "</dct:dateCopyrighted>"));
assertThat(xml,
containsString("<dct:dateSubmitted>" + modified + "</dct:dateSubmitted>"));
assertThat(xml, containsString("<dct:issued>" + modified + "</dct:issued>"));
assertThat(xml,
containsString("<dc:source>" + metacard.getResourceURI() + "</dc:source>"));
assertThat(xml,
containsString("<dc:publisher>" + metacard.getSourceId() + "</dc:publisher>"));
case SUMMARY:
// This seems weak but we only have a default mapping for modified
assertThat(xml, containsString("<dct:modified>" + modified + "</dct:modified>"));
// assertThat(xml, containsString("<dc:subject>" + metacard.getId() + "</dc:subject>"));
// assertThat(xml, containsString("<dc:format>" + metacard.getId() + "</dc:format>"));
// assertThat(xml, containsString("<dc:relation>" + metacard.getId() + "</dc:relation>"));
// assertThat(xml, containsString("<dc:abstract>" + metacard.getId() + "</dc:abstract>"));
// assertThat(xml, containsString("<dc:spatial>" + metacard.getId() + "</dc:spatial>"));
// assertThat(xml, containsString("<dc:modified>" + metacard.getId() + "</dc:modified>"));
case BRIEF:
assertThat(xml,
containsString("<dc:identifier>" + metacard.getId() + "</dc:identifier>"));
assertThat(xml, containsString("<dc:title>" + metacard.getTitle() + "</dc:title>"));
assertThat(xml,
containsString("<dc:type>" + metacard.getContentTypeName() + "</dc:type>"));
}
// TODO - assert the reverse - if brief then it shouldn't have the others
}
}