package ucar.nc2.ogc;
import net.opengis.waterml.x20.CollectionDocument;
import org.apache.xmlbeans.*;
import org.joda.time.DateTime;
import org.n52.oxf.xmlbeans.parser.LaxValidationCase;
import org.n52.oxf.xmlbeans.parser.XMLBeansParser;
import org.n52.oxf.xmlbeans.parser.XMLHandlingException;
import ucar.nc2.VariableSimpleIF;
import ucar.nc2.ft.FeatureDatasetPoint;
import ucar.nc2.ogc.waterml.NcCollectionType;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by cwardgar on 2014/04/29.
*/
public class MarshallingUtil {
/////////////////////////////////////////////// Testing Util ///////////////////////////////////////////////
public static DateTime fixedGenerationDate = null;
public static DateTime fixedResultTime = null;
/////////////////////////////////////////////// ID Creation ///////////////////////////////////////////////
private static Map<Class<?>, Integer> idsMap = new HashMap<>();
public static String createIdForType(Class<?> clazz) {
Integer numIds = idsMap.get(clazz);
if (numIds == null) {
numIds = 0;
}
String id = clazz.getSimpleName() + "." + ++numIds;
idsMap.put(clazz, numIds);
return id;
}
public static void resetIds() {
idsMap.clear();
}
/////////////////////////////////////////////// Marshalling ///////////////////////////////////////////////
public static void marshalPointDataset(FeatureDatasetPoint fdPoint, OutputStream outputStream)
throws IOException, XMLHandlingException {
marshalPointDataset(fdPoint, fdPoint.getDataVariables(), outputStream);
}
public static void marshalPointDataset(FeatureDatasetPoint fdPoint, List<VariableSimpleIF> dataVars,
OutputStream outputStream) throws IOException, XMLHandlingException {
resetIds();
CollectionDocument collectionDoc = CollectionDocument.Factory.newInstance();
NcCollectionType.initCollection(collectionDoc.addNewCollection(), fdPoint, dataVars);
writeObject(collectionDoc, outputStream, true);
}
public static void writeObject(XmlObject doc, OutputStream out, boolean validate)
throws IOException, XMLHandlingException {
// Add xsi:schemaLocation
XmlCursor cursor = doc.newCursor();
if (cursor.toFirstChild()) {
String location = "http://www.opengis.net/waterml/2.0 http://schemas.opengis.net/waterml/2.0/waterml2.xsd";
cursor.setAttributeText(new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation"), location);
}
if (validate) {
validate(doc, false);
}
doc.save(out, makeOptions());
}
private static XmlOptions makeOptions() {
XmlOptions options = new XmlOptions();
options.setSaveNamespacesFirst();
options.setSavePrettyPrint();
options.setSavePrettyPrintIndent(4);
options.setSaveAggressiveNamespaces();
options.setSaveSuggestedPrefixes(getSuggestedPrefixes());
return options;
}
private static Map<String, String> getSuggestedPrefixes() {
Map<String, String> prefixMap = new HashMap<>();
prefixMap.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
prefixMap.put("http://www.w3.org/1999/xlink", "xlink");
prefixMap.put("http://www.opengis.net/gml/3.2", "gml");
prefixMap.put("http://www.opengis.net/om/2.0", "om");
prefixMap.put("http://www.opengis.net/sampling/2.0", "sam");
prefixMap.put("http://www.opengis.net/samplingSpatial/2.0", "sams");
prefixMap.put("http://www.opengis.net/swe/2.0", "swe");
prefixMap.put("http://www.opengis.net/waterml/2.0", "wml2");
prefixMap.put("http://www.opengis.net/waterml-dr/2.0", "wml2dr");
prefixMap.put("http://www.opengis.net/gmlcov/1.0", "gmlcov");
return prefixMap;
}
/////////////////////////////////////////////// Validation ///////////////////////////////////////////////
// The validation methods in org.n52.oxf.xmlbeans.parser.XMLBeansParser are not quite what we want, but we'll
// borrow lots of code from there.
// Adapted from XMLBeansParser.validateOnParse().
public static void validate(XmlObject doc, boolean strict) throws XMLHandlingException {
XMLBeansParser.getRegisteredLaxValidationCases().clear();
if (!strict) {
// The xmlbeans validator has issues with substitution groups, particularly gml:AbstractFeature subtypes.
XMLBeansParser.registerLaxValidationCase(new GML32AbstractFeatureCase());
}
String errorString = createErrorMessage(XMLBeansParser.validate(doc));
if (errorString.length() > 0) throw new XMLHandlingException(errorString);
}
// Direct copy of XMLBeansParser.createErrorMessage().
private static String createErrorMessage(Collection<XmlError> errors) {
StringBuilder errorBuilder = new StringBuilder();
for (XmlError xmlError : errors) {
errorBuilder.append(xmlError.getMessage()).append(";");
}
if (!errors.isEmpty()) {
errorBuilder.deleteCharAt(errorBuilder.length() - 1);
}
return errorBuilder.toString();
}
/**
* Allow substitutions of gml:AbstractFeature. This lax validation lets pass every child, hence
* it checks not _if_ this is a valid substitution.
* </p>
* This is similar to org.n52.oxf.xmlbeans.parser.GMLAbstractFeatureCase, updated for GML 3.2.
*/
private static class GML32AbstractFeatureCase implements LaxValidationCase {
private static final Object FEATURE_QN = new QName("http://www.opengis.net/gml/3.2", "AbstractFeature");
@Override
public boolean shouldPass(XmlValidationError xve) {
return xve.getExpectedQNames() != null && xve.getExpectedQNames().contains(FEATURE_QN);
}
@Override
public boolean shouldPass(XmlError validationError) {
if (!(validationError instanceof XmlValidationError)) return false;
XmlValidationError xve = (XmlValidationError) validationError;
return shouldPass(xve);
}
}
private MarshallingUtil() { }
}