Commit 2f3ec624 authored by ByronCinNZ's avatar ByronCinNZ

Upgrade for GN3.6 effort thus far

parent 02da2266
/*
* Copyright (C) 2001-2016 Food and Agriculture Organization of the
* United Nations (FAO-UN), United Nations World Food Programme (WFP)
* and United Nations Environment Programme (UNEP)
*
* 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 of the License, 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
* Rome - Italy. email: geonetwork@osgeo.org
*/
package org.fao.geonet.schema.iso19139mcp14;
import org.jdom.Namespace;
/**
* Namespaces for iso19139 metadata standard.
* <p/>
* Created by francois on 3/26/14.
*/
public class ISO19139mcp14Namespaces {
public static final Namespace GCO =
Namespace.getNamespace("gco", "http://www.isotc211.org/2005/gco");
public static final Namespace SRV =
Namespace.getNamespace("srv", "http://www.isotc211.org/2005/srv");
public static final Namespace GMD =
Namespace.getNamespace("gmd", "http://www.isotc211.org/2005/gmd");
public static final Namespace GML =
Namespace.getNamespace("gml", "http://www.opengis.net/gml");
public static final Namespace MCP =
Namespace.getNamespace("mcp", "http://bluenet3.antcrc.utas.edu.au/mcp");
public static final Namespace XSI =
Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
public static final Namespace GTS =
Namespace.getNamespace("gts", "http://www.isotc211.org/2005/gts");
public static final Namespace GMX =
Namespace.getNamespace("gmx", "http://www.isotc211.org/2005/gmx");
public static final Namespace XLINK =
Namespace.getNamespace("xlink", "http://www.w3.org/1999/xlink");
}
/*
* Copyright (C) 2001-2016 Food and Agriculture Organization of the
* United Nations (FAO-UN), United Nations World Food Programme (WFP)
* and United Nations Environment Programme (UNEP)
*
* 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 of the License, 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
* Rome - Italy. email: geonetwork@osgeo.org
*/
package org.fao.geonet.schema.iso19139mcp14;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang.StringUtils;
import org.fao.geonet.kernel.schema.*;
import org.fao.geonet.utils.Log;
import org.fao.geonet.utils.Xml;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.filter.ElementFilter;
import org.jdom.xpath.XPath;
import java.util.*;
import static org.fao.geonet.schema.iso19139mcp14.ISO19139mcp14Namespaces.GCO;
import static org.fao.geonet.schema.iso19139mcp14.ISO19139mcp14Namespaces.GMD;
import static org.fao.geonet.schema.iso19139mcp14.ISO19139mcp14Namespaces.GMX;
import static org.fao.geonet.schema.iso19139mcp14.ISO19139mcp14Namespaces.SRV;
import static org.fao.geonet.schema.iso19139mcp14.ISO19139mcp14Namespaces.XLINK;
import static org.fao.geonet.schema.iso19139mcp14.ISO19139mcp14Namespaces.MCP;
/**
* Created by francois on 6/15/14.
*/
public class ISO19139mcp14SchemaPlugin
extends org.fao.geonet.kernel.schema.SchemaPlugin
implements
AssociatedResourcesSchemaPlugin,
MultilingualSchemaPlugin,
ExportablePlugin,
ISOPlugin {
public static final String IDENTIFIER = "iso19139.mcp-1.4";
public static ImmutableSet<Namespace> allNamespaces;
private static Map<String, Namespace> allTypenames;
private static Map<String, String> allExportFormats;
static {
allNamespaces = ImmutableSet.<Namespace>builder()
.add(GCO)
.add(GMD)
.add(SRV)
.add(MCP)
.build();
allTypenames = ImmutableMap.<String, Namespace>builder()
.put("csw:Record", Namespace.getNamespace("csw", "http://www.opengis.net/cat/csw/2.0.2"))
.put("mcp:MD_Metadata", ISO19139mcp14Namespaces.MCP)
.put("dcat", Namespace.getNamespace("dcat", "http://www.w3.org/ns/dcat#"))
.build();
allExportFormats = ImmutableMap.<String, String>builder()
// This is more for all basic iso19139 profiles using this bean as default
// The conversion is not available in regular iso19139 plugin.
// This is for backward compatibility.
.put("convert/to19139.xsl", "metadata-iso19139.xml")
.build();
}
public ISO19139mcp14SchemaPlugin() {
super(IDENTIFIER, allNamespaces);
}
/**
* Return sibling relation defined in aggregationInfo.
*/
public Set<AssociatedResource> getAssociatedResourcesUUIDs(Element metadata) {
String XPATH_FOR_AGGRGATIONINFO = "*//gmd:aggregationInfo/*" +
"[gmd:aggregateDataSetIdentifier/*/gmd:code " +
"and gmd:associationType/gmd:DS_AssociationTypeCode/@codeListValue!='']";
Set<AssociatedResource> listOfResources = new HashSet<AssociatedResource>();
List<?> sibs = null;
try {
sibs = Xml.selectNodes(
metadata,
XPATH_FOR_AGGRGATIONINFO,
allNamespaces.asList());
for (Object o : sibs) {
try {
if (o instanceof Element) {
Element sib = (Element) o;
Element agId = (Element) sib.getChild("aggregateDataSetIdentifier", ISO19139mcp14Namespaces.GMD)
.getChildren().get(0);
String sibUuid = getChild(agId, "code", ISO19139mcp14Namespaces.GMD)
.getChildText("CharacterString", ISO19139mcp14Namespaces.GCO);
final Element associationTypeEl = getChild(sib, "associationType", ISO19139mcp14Namespaces.GMD);
String associationType = getChild(associationTypeEl, "DS_AssociationTypeCode", ISO19139mcp14Namespaces.GMD)
.getAttributeValue("codeListValue");
final Element initiativeTypeEl = getChild(sib, "initiativeType", ISO19139mcp14Namespaces.GMD);
String initiativeType = "";
if (initiativeTypeEl != null) {
initiativeType = getChild(initiativeTypeEl, "DS_InitiativeTypeCode", ISO19139mcp14Namespaces.GMD)
.getAttributeValue("codeListValue");
}
AssociatedResource resource = new AssociatedResource(sibUuid, initiativeType, associationType);
listOfResources.add(resource);
}
} catch (Exception e) {
Log.error(Log.JEEVES, "Error getting resources UUIDs", e);
}
}
} catch (Exception e) {
Log.error(Log.JEEVES, "Error getting resources UUIDs", e);
}
return listOfResources;
}
private Element getChild(Element el, String name, Namespace namespace) {
final Element child = el.getChild(name, namespace);
if (child == null) {
return new Element(name, namespace);
}
return child;
}
@Override
public Set<String> getAssociatedParentUUIDs(Element metadata) {
ElementFilter elementFilter = new ElementFilter("parentIdentifier", ISO19139mcp14Namespaces.GMD);
return Xml.filterElementValues(
metadata,
elementFilter,
"CharacterString", ISO19139mcp14Namespaces.GCO,
null);
}
public Set<String> getAssociatedDatasetUUIDs(Element metadata) {
return getAttributeUuidrefValues(metadata, "operatesOn", SRV);
}
public Set<String> getAssociatedFeatureCatalogueUUIDs(Element metadata) {
return getAttributeUuidrefValues(metadata, "featureCatalogueCitation", ISO19139mcp14Namespaces.GMD);
}
public Set<String> getAssociatedSourceUUIDs(Element metadata) {
return getAttributeUuidrefValues(metadata, "source", ISO19139mcp14Namespaces.GMD);
}
private Set<String> getAttributeUuidrefValues(Element metadata, String tagName, Namespace namespace) {
ElementFilter elementFilter = new ElementFilter(tagName, namespace);
return Xml.filterElementValues(
metadata,
elementFilter,
null, null,
"uuidref");
}
@Override
public List<Element> getTranslationForElement(Element element, String languageIdentifier) {
final String path = ".//gmd:LocalisedCharacterString" +
"[@locale='#" + languageIdentifier + "']";
try {
XPath xpath = XPath.newInstance(path);
@SuppressWarnings("unchecked")
List<Element> matches = xpath.selectNodes(element);
return matches;
} catch (Exception e) {
Log.debug(LOGGER_NAME, getIdentifier() + ": getTranslationForElement failed " +
"on element " + Xml.getString(element) +
" using XPath '" + path +
"updatedLocalizedTextElement exception " + e.getMessage());
}
return null;
}
/**
* Add a LocalisedCharacterString to an element. In ISO19139, the translation are stored
* gmd:PT_FreeText/gmd:textGroup/gmd:LocalisedCharacterString.
*
* <pre>
* <gmd:title xsi:type="gmd:PT_FreeText_PropertyType">
* <gco:CharacterString>Template for Vector data in ISO19139 (multilingual)</gco:CharacterString>
* <gmd:PT_FreeText>
* <gmd:textGroup>
* <gmd:LocalisedCharacterString locale="#FRE">Modèle de données vectorielles en
* ISO19139 (multilingue)</gmd:LocalisedCharacterString>
* </gmd:textGroup>
* </pre>
*/
@Override
public void addTranslationToElement(Element element, String languageIdentifier, String value) {
// An ISO19139 element containing translation has an xsi:type attribute
element.setAttribute("type", "gmd:PT_FreeText_PropertyType",
Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"));
// Create a new translation for the language
Element langElem = new Element("LocalisedCharacterString", ISO19139mcp14Namespaces.GMD);
langElem.setAttribute("locale", "#" + languageIdentifier);
langElem.setText(value);
Element textGroupElement = new Element("textGroup", ISO19139mcp14Namespaces.GMD);
textGroupElement.addContent(langElem);
// Get the PT_FreeText node where to insert the translation into
Element freeTextElement = element.getChild("PT_FreeText", ISO19139mcp14Namespaces.GMD);
if (freeTextElement == null) {
freeTextElement = new Element("PT_FreeText", ISO19139mcp14Namespaces.GMD);
element.addContent(freeTextElement);
}
freeTextElement.addContent(textGroupElement);
}
/**
* Remove all multilingual aspect of an element. Keep the md language localized strings
* as default gco:CharacterString for the element.
*
* @param element
* @param langs Metadata languages. The main language MUST be the first one.
* @return
* @throws JDOMException
*/
@Override
public Element removeTranslationFromElement(Element element, List<String> langs) throws JDOMException {
String mainLanguage = langs != null && langs.size() > 0 ? langs.get(0) : "#EN";
List<Element> nodesWithStrings = (List<Element>) Xml.selectNodes(element, "*//gmd:PT_FreeText", Arrays.asList(GMD));
for(Element e : nodesWithStrings) {
// Retrieve or create the main language element
Element mainCharacterString = ((Element)e.getParent()).getChild("CharacterString", ISO19139mcp14Namespaces.GCO);
if (mainCharacterString == null) {
// create it if it does not exist
mainCharacterString = new Element("CharacterString", ISO19139mcp14Namespaces.GCO);
((Element)e.getParent()).addContent(0, mainCharacterString);
}
// Retrieve the main language value if exist
List<Element> mainLangElement = (List<Element>) Xml.selectNodes(
e,
"*//gmd:LocalisedCharacterString[@locale='" + mainLanguage + "']",
Arrays.asList(GMD));
// Set the main language value
if (mainLangElement.size() == 1) {
String mainLangString = mainLangElement.get(0).getText();
if (StringUtils.isNotEmpty(mainLangString)) {
mainCharacterString.setText(mainLangString);
} else if (mainCharacterString.getAttribute("nilReason", ISO19139mcp14Namespaces.GCO) == null){
((Element)mainCharacterString.getParent()).setAttribute("nilReason", "missing", ISO19139mcp14Namespaces.GCO);
}
} else if (StringUtils.isEmpty(mainCharacterString.getText())) {
((Element)mainCharacterString.getParent()).setAttribute("nilReason", "missing", ISO19139mcp14Namespaces.GCO);
}
}
// Remove unused lang entries
List<Element> translationNodes = (List<Element>)Xml.selectNodes(element, "*//node()[@locale]");
for(Element el : translationNodes) {
// Remove all translations if there is no or only one language requested
if(langs.size() <= 1 ||
!langs.contains(el.getAttribute("locale").getValue())) {
Element parent = (Element)el.getParent();
parent.detach();
}
}
// Remove PT_FreeText which might be emptied by above processing
for(Element el : nodesWithStrings) {
if (el.getChildren().size() == 0) {
el.detach();
}
}
return element;
}
@Override
public String getBasicTypeCharacterStringName() {
return "gco:CharacterString";
}
@Override
public Element createBasicTypeCharacterString() {
return new Element("CharacterString", ISO19139mcp14Namespaces.GCO);
}
@Override
public Element addOperatesOn(Element serviceRecord,
Map<String, String> layers,
String serviceType,
String baseUrl) {
Element root = serviceRecord
.getChild("identificationInfo", GMD)
.getChild("SV_ServiceIdentification", SRV);
if (root != null) {
// Coupling type MUST be present as it is the insertion point
// for coupledResource
Element couplingType = root.getChild("couplingType", SRV);
int coupledResourceIdx = root.indexOf(couplingType);
layers.keySet().forEach(uuid -> {
String layerName = layers.get(uuid);
// Create coupled resources elements to register all layername
// in service metadata. This information could be used to add
// interactive map button when viewing service metadata.
Element coupledResource = new Element("coupledResource", SRV);
coupledResource.setAttribute("nilReason", "synchronizedFromOGC", ISO19139mcp14Namespaces.GCO);
Element scr = new Element("SV_CoupledResource", SRV);
// Create operation according to service type
Element operation = new Element("operationName", SRV);
Element operationValue = new Element("CharacterString", GCO);
if (serviceType.startsWith("WMS"))
operationValue.setText("GetMap");
else if (serviceType.startsWith("WFS"))
operationValue.setText("GetFeature");
else if (serviceType.startsWith("WCS"))
operationValue.setText("GetCoverage");
else if (serviceType.startsWith("SOS"))
operationValue.setText("GetObservation");
operation.addContent(operationValue);
// Create identifier (which is the metadata identifier)
Element id = new Element("identifier", SRV);
Element idValue = new Element("CharacterString", GCO);
idValue.setText(uuid);
id.addContent(idValue);
// Create scoped name element as defined in CSW 2.0.2 ISO profil
// specification to link service metadata to a layer in a service.
Element scopedName = new Element("ScopedName", GCO);
scopedName.setText(layerName);
scr.addContent(operation);
scr.addContent(id);
scr.addContent(scopedName);
coupledResource.addContent(scr);
// Add coupled resource before coupling type element
if (coupledResourceIdx != -1) {
root.addContent(coupledResourceIdx, coupledResource);
}
// Add operatesOn element at the end of identification section.
Element op = new Element("operatesOn", SRV);
op.setAttribute("nilReason", "synchronizedFromOGC", GCO);
op.setAttribute("uuidref", uuid);
String hRefLink = baseUrl + "api/records/" + uuid + "/formatters/xml";
op.setAttribute("href", hRefLink, XLINK);
root.addContent(op);
});
}
return serviceRecord;
}
@Override
public List<Extent> getExtents(Element record) {
List<Extent> extents = new ArrayList<>();
ElementFilter bboxFinder = new ElementFilter("EX_GeographicBoundingBox", GMD);
@SuppressWarnings("unchecked")
Iterator<Element> bboxes = record.getDescendants(bboxFinder);
while (bboxes.hasNext()) {
Element box = bboxes.next();
try {
extents.add(new Extent(
Double.valueOf(box.getChild("westBoundLongitude", GMD).getChild("Decimal", GCO).getText()),
Double.valueOf(box.getChild("eastBoundLongitude", GMD).getChild("Decimal", GCO).getText()),
Double.valueOf(box.getChild("southBoundLatitude", GMD).getChild("Decimal", GCO).getText()),
Double.valueOf(box.getChild("northBoundLatitude", GMD).getChild("Decimal", GCO).getText())
));
} catch (NullPointerException e) {}
}
return extents;
}
@Override
public Map<String, Namespace> getCswTypeNames() {
return allTypenames;
}
@Override
public Map<String, String> getExportFormats() {
return allExportFormats;
}
/**
* Process some of the ISO elements which can have substitute.
*
* For example, a CharacterString can have a gmx:Anchor as a substitute
* to encode a text value + an extra URL. To make the transition between
* CharacterString and Anchor transparent, this method takes care of
* creating the appropriate element depending on the presence of an xlink:href attribute.
* If the attribute is empty, then a CharacterString is used, if a value is set, an Anchor is created.
*
* @param el element to process.
* @param attributeRef the attribute reference
* @param parsedAttributeName the name of the attribute, for example <code>xlink:href</code>
* @param attributeValue the attribute value
* @return
*/
@Override
public Element processElement(Element el,
String attributeRef,
String parsedAttributeName,
String attributeValue) {
if (Log.isDebugEnabled(LOGGER_NAME)) {
Log.debug(LOGGER_NAME, String.format(
"Processing element %s, attribute %s with attributeValue %s.",
el, attributeRef, attributeValue));
}
boolean elementToProcess = isElementToProcess(el);
if (elementToProcess && parsedAttributeName.equals("xlink:href")) {
boolean isEmptyLink = StringUtils.isEmpty(attributeValue);
boolean isMultilingualElement = el.getName().equals("LocalisedCharacterString");
if (isMultilingualElement) {
// The attribute provided relates to the CharacterString and not to the LocalisedCharacterString
Element targetElement = el.getParentElement().getParentElement().getParentElement()
.getChild("CharacterString", GCO);
if (targetElement != null) {
el = targetElement;
}
}
if (isEmptyLink) {
el.setNamespace(GCO).setName("CharacterString");
el.removeAttribute("href", XLINK);
return el;
} else {
el.setNamespace(GMX).setName("Anchor");
el.setAttribute("href", "", XLINK);
return el;
}
} else if (elementToProcess && StringUtils.isNotEmpty(parsedAttributeName) &&
parsedAttributeName.startsWith(":")) {
// eg. :codeSpace
el.setAttribute(parsedAttributeName.substring(1), attributeValue);
return el;
} else {
return super.processElement(el, attributeRef, parsedAttributeName, attributeValue);
}
}
/**
* Checks if an element requires processing in {@link #processElement(Element, String, String, String)}.
*
* @param el Element to check.
*
* @return boolean indicating if the element requires processing or not.
*/
protected boolean isElementToProcess(Element el) {
if (el == null) return false;
return elementsToProcess.contains(el.getQualifiedName());
}
}
...@@ -21,9 +21,9 @@ ...@@ -21,9 +21,9 @@
* Rome - Italy. email: geonetwork@osgeo.org * Rome - Italy. email: geonetwork@osgeo.org
*/ */
import iso19139.SummaryFactory import iso19139mcp14.SummaryFactory
def isoHandlers = new iso19139.Handlers(handlers, f, env) def isoHandlers = new iso19139mcp14.Handlers(handlers, f, env)
SummaryFactory.summaryHandler({it.parent() is it.parent()}, isoHandlers) SummaryFactory.summaryHandler({it.parent() is it.parent()}, isoHandlers)
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* Rome - Italy. email: geonetwork@osgeo.org * Rome - Italy. email: geonetwork@osgeo.org
*/ */
package iso19139 package iso19139mcp14
import org.fao.geonet.domain.ISODate import org.fao.geonet.domain.ISODate
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* Rome - Italy. email: geonetwork@osgeo.org * Rome - Italy. email: geonetwork@osgeo.org
*/ */
package iso19139 package iso19139mcp14
import org.fao.geonet.api.records.formatters.groovy.Environment import org.fao.geonet.api.records.formatters.groovy.Environment
import org.fao.geonet.api.records.formatters.groovy.MapConfig import org.fao.geonet.api.records.formatters.groovy.MapConfig
...@@ -31,17 +31,17 @@ public class Handlers { ...@@ -31,17 +31,17 @@ public class Handlers {