/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * 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 org.constellation.metadata.io; import org.apache.sis.xml.Namespaces; import org.constellation.util.NodeUtilities; import org.geotoolkit.temporal.object.TemporalUtilities; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.TimeZone; import java.util.logging.Level; import static org.geotoolkit.ows.xml.OWSExceptionCode.NO_APPLICABLE_CODE; /** * * @author Guilhem Legal (Geomatys) */ public abstract class DomMetadataReader extends AbstractMetadataReader { /** * A date formatter used to display the Date object for Dublin core translation. */ private static final DateFormat FORMATTER; static { FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); FORMATTER.setTimeZone(TimeZone.getTimeZone("GMT+2")); } protected final DocumentBuilderFactory dbf; protected final XMLInputFactory xif = XMLInputFactory.newFactory(); public DomMetadataReader(final boolean isCacheEnabled, final boolean isThreadEnabled) { super(isCacheEnabled, isThreadEnabled); dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); } protected MetadataType getMetadataType(final InputStream metadataStream, final boolean reset) throws IOException, XMLStreamException { final String rootName; if (reset){ metadataStream.mark(0); } final XMLStreamReader xsr = xif.createXMLStreamReader(metadataStream); xsr.nextTag(); rootName = xsr.getLocalName(); xsr.close(); if (reset) { metadataStream.reset(); } switch (rootName) { case "MD_Metadata": case "MI_Metadata": return MetadataType.ISO_19115; case "Record": return MetadataType.DUBLINCORE; case "SensorML": return MetadataType.SENSORML; case "RegistryObject": case "AdhocQuery": case "Association": case "RegistryPackage": case "Registry": case "ExtrinsicObject": case "RegistryEntry": return MetadataType.EBRIM; default: return MetadataType.NATIVE; } // TODO complete other metadata type } protected MetadataType getMetadataType(final Reader metadataReader, final boolean reset) throws IOException, XMLStreamException { final String rootName; if (reset){ metadataReader.mark(0); } final XMLStreamReader xsr = xif.createXMLStreamReader(metadataReader); xsr.nextTag(); rootName = xsr.getLocalName(); xsr.close(); if (reset) { metadataReader.reset(); } switch (rootName) { case "MD_Metadata": case "MI_Metadata": return MetadataType.ISO_19115; case "Record": return MetadataType.DUBLINCORE; case "SensorML": return MetadataType.SENSORML; case "RegistryObject": case "AdhocQuery": case "Association": case "RegistryPackage": case "Registry": case "ExtrinsicObject": case "RegistryEntry": return MetadataType.EBRIM; default: return MetadataType.NATIVE; } // TODO complete other metadata type } protected Node getNodeFromFile(final File metadataFile) throws MetadataIoException { try { final DocumentBuilder docBuilder = dbf.newDocumentBuilder(); final Document document = docBuilder.parse(metadataFile); return document.getDocumentElement(); } catch (ParserConfigurationException | SAXException | IOException ex) { throw new MetadataIoException("The metadata file : " + metadataFile.getName() + ".xml can not be read", ex, NO_APPLICABLE_CODE); } } protected Node getNodeFromStream(final InputStream stream) throws MetadataIoException { try { final DocumentBuilder docBuilder = dbf.newDocumentBuilder(); final Document document = docBuilder.parse(stream); return document.getDocumentElement(); } catch (ParserConfigurationException | SAXException | IOException ex) { throw new MetadataIoException("unable to parse the metadata", ex, NO_APPLICABLE_CODE); } } protected Node getNodeFromReader(final Reader reader) throws MetadataIoException { try { final InputSource source = new InputSource(reader); final DocumentBuilder docBuilder = dbf.newDocumentBuilder(); final Document document = docBuilder.parse(source); return document.getDocumentElement(); } catch (ParserConfigurationException | SAXException | IOException ex) { throw new MetadataIoException("unable to parse the metadata", ex, NO_APPLICABLE_CODE); } } protected String formatDate(final String modValue) { try { final Date d = TemporalUtilities.parseDate(modValue); String dateValue; synchronized (FORMATTER) { dateValue = FORMATTER.format(d); } dateValue = dateValue.substring(0, dateValue.length() - 2); dateValue = dateValue + ":00"; return dateValue; } catch (ParseException ex) { LOGGER.log(Level.WARNING, "unable to parse date: {0}", modValue); } return null; } /** * Apply the elementSet (Brief, Summary or full) or the custom elementSetName on the specified record. * * @param record A dublinCore record. * @param type The ElementSetType to apply on this record. * @param elementName A list of QName corresponding to the requested attribute. this parameter is ignored if type is not null. * * @return A record object. * @throws MetadataIoException If the type and the element name are null. */ protected Node applyElementSetNode(final Node record, final ElementSetType type, final List<QName> elementName) throws MetadataIoException { final DocumentBuilder docBuilder; try { docBuilder = dbf.newDocumentBuilder(); } catch (ParserConfigurationException ex) { throw new MetadataIoException(ex); } final Document document = docBuilder.newDocument(); if (type != null) { if (type.equals(ElementSetType.SUMMARY)) { final Element sumRoot = document.createElementNS(Namespaces.CSW, "SummaryRecord"); final List<String> identifierValues = NodeUtilities.getValuesFromPath(record, "/csw:Record/dc:identifier"); final List<Node> identifiers = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "identifier", identifierValues, true); NodeUtilities.appendChilds(sumRoot, identifiers); final List<String> titleValues = NodeUtilities.getValuesFromPath(record, "/csw:Record/dc:title"); final List<Node> titles = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "title", titleValues, true); NodeUtilities.appendChilds(sumRoot, titles); final List<String> typeValues = NodeUtilities.getValuesFromPath(record, "/csw:Record/dc:type"); final List<Node> types = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "type", typeValues, false); NodeUtilities.appendChilds(sumRoot, types); final List<String> subValues = NodeUtilities.getValuesFromPath(record, "/csw:Record/dc:subject"); final List<Node> subjects = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "subject", subValues, false); NodeUtilities.appendChilds(sumRoot, subjects); final List<String> formValues = NodeUtilities.getValuesFromPath(record, "/csw:Record/dc:format"); final List<Node> formats = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "format", formValues, false); NodeUtilities.appendChilds(sumRoot, formats); final List<String> modValues = NodeUtilities.getValuesFromPath(record, "/csw:Record/dc:modified"); final List<Node> modifieds = NodeUtilities.buildNodes(document, "http://purl.org/dc/terms/", "modified", modValues, false); NodeUtilities.appendChilds(sumRoot, modifieds); final List<String> absValues = NodeUtilities.getValuesFromPath(record, "/csw:Record/dc:abstract"); final List<Node> abstracts = NodeUtilities.buildNodes(document, "http://purl.org/dc/terms/", "abstract", absValues, false); NodeUtilities.appendChilds(sumRoot, abstracts); final List<Node> origBboxes = NodeUtilities.getNodeFromPath(record, "/ows:BoundingBox"); for (Node origBbox : origBboxes) { Node n = document.importNode(origBbox, true); NodeUtilities.appendChilds(sumRoot, Arrays.asList(n)); } return sumRoot; } else if (type.equals(ElementSetType.BRIEF)) { final Element briefRoot = document.createElementNS(Namespaces.CSW, "BriefRecord"); final List<String> identifierValues = NodeUtilities.getValuesFromPath(record, "/csw:Record/dc:identifier"); final List<Node> identifiers = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "identifier", identifierValues, true); NodeUtilities.appendChilds(briefRoot, identifiers); final List<String> titleValues = NodeUtilities.getValuesFromPath(record, "/csw:Record/dc:title"); final List<Node> titles = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "title", titleValues, true); NodeUtilities.appendChilds(briefRoot, titles); final List<String> typeValues = NodeUtilities.getValuesFromPath(record, "/csw:Record/dc:type"); final List<Node> types = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "type", typeValues, false); NodeUtilities.appendChilds(briefRoot, types); final List<Node> origBboxes = NodeUtilities.getNodeFromPath(record, "/csw:Record/ows:BoundingBox"); for (Node origBbox : origBboxes) { Node n = document.importNode(origBbox, true); NodeUtilities.appendChilds(briefRoot, Arrays.asList(n)); } return briefRoot; } else { return record; } } else if (elementName != null) { final Element recRoot = document.createElementNS(Namespaces.CSW, "Record"); for (QName qn : elementName) { if (qn != null) { final List<Node> origs = NodeUtilities.getNodeFromPath(record, "/dc:" + qn.getLocalPart()); for (Node orig : origs) { Node n = document.importNode(orig, true); NodeUtilities.appendChilds(recRoot, Arrays.asList(n)); } } else { LOGGER.warning("An elementName was null."); } } return recRoot; } else { throw new MetadataIoException("No ElementSet or Element name specified"); } } /** * Return all the String values corresponding to the specified list of path through the metadata. * * @param paths * @return * @throws MetadataIoException */ protected List<String> getAllValuesFromPaths(final List<String> paths) throws MetadataIoException { final List<String> result = new ArrayList<>(); final List<String> ids = getAllIdentifiers(); for (String metadataID : ids) { final Node metadata = getMetadata(metadataID, MetadataType.ISO_19115); final List<Object> value = NodeUtilities.extractValues(metadata, paths); if (value != null && !value.equals(Arrays.asList("null"))) { for (Object obj : value){ result.add(obj.toString()); } } } Collections.sort(result); return result; } @Override public abstract List<Node> getAllEntries() throws MetadataIoException; }