thredds.client.catalog.builder.CatalogBuilder Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
* See LICENSE for license information.
*/
package thredds.client.catalog.builder;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.input.SAXBuilder;
import thredds.client.catalog.*;
import ucar.nc2.constants.CDM;
import ucar.nc2.constants.DataFormatType;
import ucar.nc2.constants.FeatureType;
import ucar.nc2.time.Calendar;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.time.CalendarDateFormatter;
import ucar.nc2.units.DateRange;
import ucar.nc2.units.DateType;
import ucar.nc2.units.TimeDuration;
import ucar.nc2.util.URLnaming;
import ucar.unidata.util.StringUtil2;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
/**
* Builds client Catalogs using JDOM2
* Non validating.
*
* @author caron
* @since 1/8/2015
*/
public class CatalogBuilder {
static private org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CatalogBuilder.class);
//////////////////////////////////////////////////////////////////////////////////
// used when reading from XML
private Map serviceMap = new HashMap<>(); // LOOK why not instead of services ?
protected Formatter errlog = new Formatter();
protected boolean fatalError = false;
public Catalog buildFromLocation(String location, URI baseURI) throws IOException {
location = StringUtil2.replace(location, "\\", "/");
if (baseURI == null) {
try {
baseURI = new URI(location);
} catch (URISyntaxException e) {
errlog.format("Bad location = '%s' err='%s'%n", location, e.getMessage());
fatalError = true;
return null;
}
}
this.baseURI = baseURI;
readXML(location);
return fatalError ? null : makeCatalog();
}
public Catalog buildFromURI(URI uri) throws IOException {
this.baseURI = uri;
readXML(uri);
return fatalError ? null : makeCatalog();
}
public Catalog buildFromCatref(CatalogRef catref) throws IOException {
URI catrefURI = catref.getURI();
if (catrefURI == null) {
errlog.format("Catref doesnt have valid UrlPath=%s%n", catref.getUrlPath());
fatalError = true;
return null;
}
this.baseURI = catrefURI;
Catalog result = buildFromURI(catrefURI);
catref.setRead(!fatalError);
return fatalError ? null : result;
}
public Catalog buildFromString(String catalogAsString, URI docBaseUri) throws IOException {
this.baseURI = docBaseUri;
readXMLfromString(catalogAsString);
return fatalError ? null : makeCatalog();
}
public Catalog buildFromStream(InputStream stream, URI docBaseUri) throws IOException {
this.baseURI = docBaseUri;
readXML(stream);
return fatalError ? null : makeCatalog();
}
public Catalog buildFromJdom(Element root, URI docBaseUri) throws IOException {
this.baseURI = docBaseUri;
readCatalog(root);
return fatalError ? null : makeCatalog();
}
public String getErrorMessage() {
return errlog.toString();
}
public String getValidationMessage() {
return errlog.toString();
}
public boolean hasFatalError() {
return fatalError;
}
////////////////////////////////////////////////////
protected String name, version;
protected CalendarDate expires;
protected URI baseURI;
protected List properties;
protected List services;
protected List datasetBuilders;
public CatalogBuilder() {}
public CatalogBuilder(Catalog from) {
this.name = from.getName();
this.expires = from.getExpires();
this.baseURI = from.getBaseURI();
this.properties = from.getProperties();
this.services = from.getServices(); // ??
}
public void setName(String name) {
this.name = name;
}
public void setBaseURI(URI baseURI) {
this.baseURI = baseURI;
}
public void setExpires(CalendarDate expires) {
this.expires = expires;
}
public void setVersion(String version) {
this.version = version;
}
public void addProperty(Property p) {
if (p == null) return;
if (properties == null) properties = new ArrayList<>();
if (!properties.contains(p)) // O(n^2) but only done once
properties.add(p);
}
public void addService(Service s) {
if (s == null) return;
if (services == null) services = new ArrayList<>();
if (!services.contains(s) && !containsService(s.getName()))
services.add(s);
}
private boolean containsService(String name) {
if (name == null) return false;
if (services == null) return false;
for (Service s : services) {
if (name.equals(s.getName()))
return true;
}
return false;
}
public void removeAnyService() {
services = null;
}
public void addDataset(DatasetBuilder d) {
if (d == null) return;
if (datasetBuilders == null) datasetBuilders = new ArrayList<>();
datasetBuilders.add(d);
}
////////////////////////////////////////////////////////////////
public Catalog makeCatalog() {
setServices(getDatasets());
Map flds = setFields();
return new Catalog(baseURI, name, flds, datasetBuilders);
}
// pull services out of the datasets and into the catalog
private void setServices(Iterable dsIter) {
for (DatasetBuilder dsb : dsIter) {
for (Service s : dsb.getServices())
addService(s);
setServices(dsb.getDatasets()); // recurse
}
}
protected Map setFields() {
Map flds = new HashMap<>(10);
if (expires != null) flds.put(Catalog.Expires, expires);
if (version != null) flds.put(Catalog.Version, version);
if (services != null) flds.put(Catalog.Services, services);
if (properties != null) flds.put(Dataset.Properties, properties);
return flds;
}
///////////////////////////////////////////////////////////
// get
@Nullable
public DatasetBuilder getTopDataset() {
if (datasetBuilders == null) return null;
return datasetBuilders.get(0);
}
public Iterable getDatasets() {
if (datasetBuilders != null) return datasetBuilders;
return new ArrayList<>(0);
}
public boolean hasService(String name) {
if (services == null) return false;
for (Service s : services)
if (s.getName().equalsIgnoreCase(name)) return true;
return false;
}
public boolean hasServiceInDataset(String name) {
for (DatasetBuilder dsb : datasetBuilders) {
for (Service s : dsb.getServices())
if (s.getName().equalsIgnoreCase(name)) return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////
// JDOM
/* private void readXML(String fileName) throws IOException {
try {
StAXStreamBuilder staxBuilder = new StAXStreamBuilder();
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new FileInputStream(fileName));
org.jdom2.Document jdomDoc = staxBuilder.build(xmlStreamReader);
readCatalog(jdomDoc.getRootElement());
} catch (Exception e) {
errlog.format("failed to read catalog at '%s' err='%s'%n", fileName, e);
logger.error("failed to read catalog at " + fileName, e);
// e.printStackTrace();
fatalError = true;
}
} */
private void readXML(String location) throws IOException {
try {
SAXBuilder saxBuilder = new SAXBuilder();
org.jdom2.Document jdomDoc = saxBuilder.build(location);
readCatalog(jdomDoc.getRootElement());
} catch (Exception e) {
errlog.format("failed to read catalog at '%s' err='%s'%n", location, e);
logger.error("failed to read catalog at " + location, e);
// e.printStackTrace();
fatalError = true;
}
}
private void readXML(URI uri) throws IOException {
try {
SAXBuilder saxBuilder = new SAXBuilder();
org.jdom2.Document jdomDoc = saxBuilder.build(uri.toURL());
readCatalog(jdomDoc.getRootElement());
} catch (Exception e) {
errlog.format("failed to read catalog at '%s' err='%s'%n", uri.toString(), e);
logger.error("failed to read catalog at " + uri.toString(), e);
// e.printStackTrace();
fatalError = true;
}
}
private void readXMLfromString(String catalogAsString) throws IOException {
try {
StringReader in = new StringReader(catalogAsString);
SAXBuilder saxBuilder = new SAXBuilder(); // LOOK non-validating
org.jdom2.Document jdomDoc = saxBuilder.build(in);
readCatalog(jdomDoc.getRootElement());
} catch (Exception e) {
errlog.format("failed to read catalogAsString err='%s'%n", e);
logger.error("failed to read catalogAsString at" + baseURI.toString(), e);
e.printStackTrace();
fatalError = true;
}
}
private void readXML(InputStream stream) throws IOException {
try {
SAXBuilder saxBuilder = new SAXBuilder();
org.jdom2.Document jdomDoc = saxBuilder.build(stream);
readCatalog(jdomDoc.getRootElement());
} catch (Exception e) {
errlog.format("failed to read catalogAsString err='%s'%n", e);
logger.error("failed to read catalogAsString at" + baseURI.toString(), e);
e.printStackTrace();
fatalError = true;
}
}
/*
*/
private void readCatalog(Element catalogElem) {
String name = catalogElem.getAttributeValue("name");
String catSpecifiedBaseURL = catalogElem.getAttributeValue("base"); // LOOK what is this ??
String expiresS = catalogElem.getAttributeValue("expires");
String version = catalogElem.getAttributeValue("version");
CalendarDate expires = null;
if (expiresS != null) {
try {
expires = CalendarDateFormatter.isoStringToCalendarDate(null, expiresS);
} catch (Exception e) {
errlog.format("bad expires date '%s' err='%s'%n", expiresS, e.getMessage());
}
}
if (catSpecifiedBaseURL != null) {
try {
URI userSpecifiedBaseUri = new URI(catSpecifiedBaseURL);
this.baseURI = userSpecifiedBaseUri;
} catch (URISyntaxException e) {
errlog.format("readCatalog(): bad catalog specified base URI='%s' %n", catSpecifiedBaseURL);
}
}
setName(name);
setExpires(expires);
setVersion(version);
// read top-level services
java.util.List sList = catalogElem.getChildren("service", Catalog.defNS);
for (Element e : sList) {
addService(readService(e));
}
// read top-level properties
java.util.List pList = catalogElem.getChildren("property", Catalog.defNS);
for (Element e : pList) {
addProperty(readProperty(e));
}
// look for top-level dataset and catalogRefs elements (keep them in order)
java.util.List allChildren = catalogElem.getChildren();
for (Element e : allChildren) {
if (e.getName().equals("dataset")) {
addDataset(readDataset(null, e));
} else if (e.getName().equals("catalogRef")) {
addDataset(readCatalogRef(null, e));
} else {
addDataset(buildOtherDataset(null, e));
}
}
}
// for overridding
protected DatasetBuilder buildOtherDataset(DatasetBuilder parent, Element dsElem) {
return null;
}
/*
// whyd we do that ?
*/
protected AccessBuilder readAccess(DatasetBuilder dataset, Element accessElem) {
String urlPath = accessElem.getAttributeValue("urlPath");
String serviceName = accessElem.getAttributeValue("serviceName");
String dataFormat = accessElem.getAttributeValue("dataFormat");
Service s = serviceMap.get(serviceName);
if (s == null) {
errlog.format("Cant find service name='%s'%n", serviceName);
}
return new AccessBuilder(dataset, urlPath, s, dataFormat, readDataSize(accessElem));
}
protected Property readProperty(Element s) {
String name = s.getAttributeValue("name");
String value = s.getAttributeValue("value");
return new Property(name, value);
}
/*
*/
protected Service readService(Element s) {
String name = s.getAttributeValue("name");
String typeS = s.getAttributeValue("serviceType");
String serviceBase = s.getAttributeValue("base");
String suffix = s.getAttributeValue("suffix");
String desc = s.getAttributeValue("desc");
ServiceType type = ServiceType.getServiceTypeIgnoreCase(typeS);
if (type == null) {
errlog.format(" non-standard service type = '%s'%n", typeS);
}
List properties = null;
List propertyList = s.getChildren("property", Catalog.defNS);
for (Element e : propertyList) {
if (properties == null) properties = new ArrayList<>();
properties.add(readProperty(e));
}
// nested services
List services = null;
java.util.List serviceList = s.getChildren("service", Catalog.defNS);
for (Element e : serviceList) {
if (services == null) services = new ArrayList<>();
services.add(readService(e));
}
Service result = new Service(name, serviceBase, typeS, desc, suffix, services, properties);
serviceMap.put(name, result);
return result;
}
/*
*/
protected DatasetBuilder readCatalogRef(DatasetBuilder parent, Element catRefElem) {
String title = catRefElem.getAttributeValue("title", Catalog.xlinkNS);
if (title == null) title = catRefElem.getAttributeValue("name");
String href = catRefElem.getAttributeValue("href", Catalog.xlinkNS);
CatalogRefBuilder catRef = new CatalogRefBuilder(parent);
readDatasetInfo( catRef, catRefElem);
catRef.setTitle(title);
catRef.setHref(href);
return catRef;
}
/*
*/
protected DatasetBuilder readDataset(DatasetBuilder parent, Element dsElem) {
DatasetBuilder dataset = new DatasetBuilder(parent);
readDatasetInfo(dataset, dsElem);
// look for access elements
java.util.List aList = dsElem.getChildren("access", Catalog.defNS);
for (Element e : aList) {
dataset.addAccess(readAccess(dataset, e));
}
// look for nested dataset and catalogRefs elements (keep them in order)
java.util.List allChildren = dsElem.getChildren();
for (Element e : allChildren) {
if (e.getName().equals("dataset")) {
dataset.addDataset(readDataset(dataset, e));
} else if (e.getName().equals("catalogRef")) {
dataset.addDataset(readCatalogRef(dataset, e));
} else {
dataset.addDataset( buildOtherDataset(dataset, e));
}
}
return dataset;
}
protected void readDatasetInfo(DatasetBuilder dataset, Element dsElem) {
// read attributes
String name = dsElem.getAttributeValue("name");
if (name == null) {
if (dsElem.getName().equals("catalogRef"))
dataset.setName("");
else
errlog.format(" ** warning: dataset must have a name = '%s'%n", dsElem);
} else {
dataset.setName(name);
}
dataset.put( Dataset.Alias, dsElem.getAttributeValue("alias"));
dataset.put( Dataset.Authority, dsElem.getAttributeValue("authority"));
dataset.put( Dataset.CollectionType, dsElem.getAttributeValue("collectionType"));
dataset.put( Dataset.Id, dsElem.getAttributeValue("ID"));
dataset.putInheritedField( Dataset.RestrictAccess, dsElem.getAttributeValue("restrictAccess"));
dataset.put( Dataset.ServiceName, dsElem.getAttributeValue("serviceName"));
dataset.put( Dataset.UrlPath, dsElem.getAttributeValue("urlPath"));
String dataTypeName = dsElem.getAttributeValue("dataType");
dataset.put( Dataset.FeatureType, dataTypeName);
if (dataTypeName != null) {
FeatureType dataType = FeatureType.getType(dataTypeName.toUpperCase());
if (dataType == null) {
errlog.format(" ** warning: non-standard data type = '%s'%n", dataTypeName);
}
}
String harvest = dsElem.getAttributeValue("harvest");
if (harvest != null && harvest.equalsIgnoreCase("true")) dataset.put(Dataset.Harvest, Boolean.TRUE);
// catalog.addDatasetByID(dataset); // LOOK need to do immed for alias processing
// read elements
readThreddsMetadataGroup(dataset.flds, dataset, dsElem);
}
/*
*/
protected void readThreddsMetadataGroup(Map flds, DatasetBuilder dataset, Element parent) {
List list;
// look for creators - kind of a Source
list = parent.getChildren("creator", Catalog.defNS);
for (Element e : list) {
DatasetBuilder.addToList(flds, Dataset.Creators, readSource(e));
}
// look for contributors
list = parent.getChildren("contributor", Catalog.defNS);
for (Element e : list) {
DatasetBuilder.addToList(flds, Dataset.Contributors, readContributor(e));
}
// look for dates
list = parent.getChildren("date", Catalog.defNS);
for (Element e : list) {
DatasetBuilder.addToList(flds, Dataset.Dates, readDate(e, null));
}
// look for documentation
list = parent.getChildren("documentation", Catalog.defNS);
for (Element e : list) {
DatasetBuilder.addToList(flds, Dataset.Documentation, readDocumentation(e));
}
// look for keywords - kind of a controlled vocabulary
list = parent.getChildren("keyword", Catalog.defNS);
for (Element e : list) {
DatasetBuilder.addToList(flds, Dataset.Keywords, readControlledVocabulary(e));
}
// look for metadata elements
list = parent.getChildren("metadata", Catalog.defNS);
for (Element e : list) {
DatasetBuilder.addToList(flds, Dataset.MetadataOther, readMetadata(flds, dataset, e));
}
// look for projects - kind of a controlled vocabulary
list = parent.getChildren("project", Catalog.defNS);
for (Element e : list) {
DatasetBuilder.addToList(flds, Dataset.Projects, readControlledVocabulary(e));
}
// look for properties
list = parent.getChildren("property", Catalog.defNS);
for (Element e : list) {
DatasetBuilder.addToList(flds, Dataset.Properties, readProperty(e));
}
// look for publishers - kind of a Source
list = parent.getChildren("publisher", Catalog.defNS);
for (Element e : list) {
DatasetBuilder.addToList(flds, Dataset.Publishers, readSource(e));
}
// look for variables
list = parent.getChildren("variables", Catalog.defNS);
for (Element e : list) {
DatasetBuilder.addToList(flds, Dataset.VariableGroups, readVariables(e));
}
// can only be one each of these kinds
ThreddsMetadata.GeospatialCoverage gc = readGeospatialCoverage(parent.getChild("geospatialCoverage", Catalog.defNS));
if (gc != null) flds.put(Dataset.GeospatialCoverage, gc);
DateRange tc = readTimeCoverage(parent.getChild("timeCoverage", Catalog.defNS));
if (tc != null) flds.put(Dataset.TimeCoverage, tc);
Element serviceNameElem = parent.getChild("serviceName", Catalog.defNS);
if (serviceNameElem != null) flds.put(Dataset.ServiceName, serviceNameElem.getText());
Element authElem = parent.getChild("authority", Catalog.defNS);
if (authElem != null) flds.put(Dataset.Authority, authElem.getText());
Element dataTypeElem = parent.getChild("dataType", Catalog.defNS);
if (dataTypeElem != null) {
String dataTypeName = dataTypeElem.getText();
flds.put(Dataset.FeatureType, dataTypeName);
if ((dataTypeName != null) && (dataTypeName.length() > 0)) {
FeatureType dataType = FeatureType.getType(dataTypeName.toUpperCase());
if (dataType == null) {
errlog.format(" ** warning: non-standard feature type = '%s'%n", dataTypeName);
}
}
}
Element dataFormatElem = parent.getChild("dataFormat", Catalog.defNS);
if (dataFormatElem != null) {
String dataFormatTypeName = dataFormatElem.getText();
if ((dataFormatTypeName != null) && (dataFormatTypeName.length() > 0)) {
DataFormatType dataFormatType = DataFormatType.getType(dataFormatTypeName);
if (dataFormatType == null) {
errlog.format(" ** warning: non-standard dataFormat type = '%s'%n", dataFormatTypeName);
}
flds.put(Dataset.DataFormatType, dataFormatTypeName);
}
}
long size = readDataSize(parent);
if (size > 0)
flds.put(Dataset.DataSize, size);
// LOOK: we seem to have put a variableMap element not contained by
ThreddsMetadata.UriResolved mapUri = readUri(parent.getChild("variableMap", Catalog.defNS), "variableMap");
if (mapUri != null)
flds.put(Dataset.VariableMapLinkURI, mapUri);
}
protected ThreddsMetadata.Contributor readContributor(Element elem) {
if (elem == null) return null;
return new ThreddsMetadata.Contributor(elem.getText(), elem.getAttributeValue("role"));
}
protected long readDataSize(Element parent) {
Element elem = parent.getChild("dataSize", Catalog.defNS);
if (elem == null) return -1;
double size;
String sizeS = elem.getText();
try {
size = Double.parseDouble(sizeS);
} catch (NumberFormatException e) {
errlog.format(" ** Parse error: Bad double format in size element = '%s'%n", sizeS);
return -1;
}
String units = elem.getAttributeValue("units");
char c = Character.toUpperCase(units.charAt(0));
if (c == 'K') size *= 1000;
else if (c == 'M') size *= 1000 * 1000;
else if (c == 'G') size *= 1000 * 1000 * 1000;
else if (c == 'T') size *= 1000.0 * 1000 * 1000 * 1000;
else if (c == 'P') size *= 1000.0 * 1000 * 1000 * 1000 * 1000;
return (long) size;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
protected Documentation readDocumentation(Element s) {
String href = s.getAttributeValue("href", Catalog.xlinkNS);
String title = s.getAttributeValue("title", Catalog.xlinkNS);
String type = s.getAttributeValue("type"); // not XLink type
String content = s.getTextNormalize();
URI uri = null;
if (href != null) {
try {
uri = Catalog.resolveUri(baseURI, href);
} catch (Exception e) {
errlog.format(" ** Invalid documentation href = '%s' err='%s'%n", href, e.getMessage());
}
}
return new Documentation(href, uri, title, type, content);
}
protected double readDouble(Element elem) {
if (elem == null) return Double.NaN;
String text = elem.getText();
try {
return Double.parseDouble(text);
} catch (NumberFormatException e) {
errlog.format(" ** Parse error: Bad double format = '%s'%n", text);
return Double.NaN;
}
}
/*
*/
protected ThreddsMetadata.GeospatialCoverage readGeospatialCoverage(Element gcElem) {
if (gcElem == null) return null;
String zpositive = gcElem.getAttributeValue("zpositive");
ThreddsMetadata.GeospatialRange northsouth = readGeospatialRange(gcElem.getChild("northsouth", Catalog.defNS), CDM.LAT_UNITS);
ThreddsMetadata.GeospatialRange eastwest = readGeospatialRange(gcElem.getChild("eastwest", Catalog.defNS), CDM.LON_UNITS);
ThreddsMetadata.GeospatialRange updown = readGeospatialRange(gcElem.getChild("updown", Catalog.defNS), "m");
// look for names
List names = new ArrayList<>();
java.util.List list = gcElem.getChildren("name", Catalog.defNS);
for (Element e : list) {
ThreddsMetadata.Vocab name = readControlledVocabulary(e);
names.add(name);
}
return new ThreddsMetadata.GeospatialCoverage(eastwest, northsouth, updown, names, zpositive);
}
protected ThreddsMetadata.GeospatialRange readGeospatialRange(Element spElem, String defUnits) {
if (spElem == null) return null;
double start = readDouble(spElem.getChild("start", Catalog.defNS));
double size = readDouble(spElem.getChild("size", Catalog.defNS));
double resolution = readDouble(spElem.getChild("resolution", Catalog.defNS));
String units = spElem.getChildText("units", Catalog.defNS);
if (units == null) units = defUnits;
return new ThreddsMetadata.GeospatialRange(start, size, resolution, units);
}
/*
*/
protected ThreddsMetadata.MetadataOther readMetadata(Map flds, DatasetBuilder dataset, Element mdataElement) {
// there are 6 cases to deal with: threddsNamespace vs not & inline vs Xlink & (if thredds) inherited or not
Namespace namespace;
List inlineElements = mdataElement.getChildren();
if (inlineElements.size() > 0) // look at the namespace of the children, if they exist
namespace = ((Element) inlineElements.get(0)).getNamespace();
else
namespace = mdataElement.getNamespace(); // will be thredds
String mtype = mdataElement.getAttributeValue("metadataType");
String href = mdataElement.getAttributeValue("href", Catalog.xlinkNS);
String title = mdataElement.getAttributeValue("title", Catalog.xlinkNS);
String inheritedS = mdataElement.getAttributeValue("inherited");
boolean inherited = (inheritedS != null) && inheritedS.equalsIgnoreCase("true");
boolean isThreddsNamespace = ((mtype == null) || mtype.equalsIgnoreCase("THREDDS")) && namespace.getURI().equals(Catalog.CATALOG_NAMESPACE_10);
// the case where its not ThreddsMetadata
if (!isThreddsNamespace) {
if (inlineElements.size() > 0) {
// just hold onto the jdom elements as the "content"
return new ThreddsMetadata.MetadataOther( mtype, namespace.getURI(), namespace.getPrefix(), inherited, mdataElement);
} else { // otherwise it must be an Xlink
return new ThreddsMetadata.MetadataOther(href, title, mtype, namespace.getURI(), namespace.getPrefix(), inherited);
}
}
// the case where its ThreddsMetadata
Map useFlds;
if (inherited) {
// the case where its inherited ThreddsMetadata: gonna put stuff in the tmi.
ThreddsMetadata tmi = (ThreddsMetadata) dataset.get(Dataset.ThreddsMetadataInheritable);
if (tmi == null) {
tmi = new ThreddsMetadata();
dataset.put(Dataset.ThreddsMetadataInheritable, tmi);
}
useFlds = tmi.getFlds();
} else {
// the case where its non-inherited ThreddsMetadata: gonna put stuff directly into the dataset
useFlds = flds;
}
readThreddsMetadataGroup(useFlds, dataset, mdataElement);
// also need to capture any XLinks. see http://www.unidata.ucar.edu/software/thredds/v4.6/tds/catalog/InvCatalogSpec.html#metadataElement
// in this case we just suck it in as if it was inline
if (href != null) {
try {
URI xlinkUri = Catalog.resolveUri(baseURI, href);
Element remoteMdata = readMetadataFromUrl(xlinkUri);
return readMetadata(useFlds, dataset, remoteMdata);
} catch (Exception ioe) {
errlog.format("Cant read in referenced metadata %s err=%s%n", href, ioe.getMessage());
}
}
return null; // ThreddsMetadata.MetadataOther was directly added
}
private Element readMetadataFromUrl(java.net.URI uri) throws java.io.IOException {
SAXBuilder saxBuilder = new SAXBuilder();
Document doc;
try {
doc = saxBuilder.build(uri.toURL());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
return doc.getRootElement();
}
/*
*/
protected ThreddsMetadata.Source readSource(Element elem) {
if (elem == null) return null;
ThreddsMetadata.Vocab name = readControlledVocabulary(elem.getChild("name", Catalog.defNS));
Element contact = elem.getChild("contact", Catalog.defNS);
if (contact == null) {
errlog.format(" ** Parse error: Missing contact element in = '%s'%n", elem.getName());
return null;
}
return new ThreddsMetadata.Source(name, contact.getAttributeValue("url"), contact.getAttributeValue("email"));
}
/*
Must conform to complete udunits date string, eg "20 days since 1991-01-01"
Must conform to udunits time duration, eg "20.1 hours"
*/
protected DateRange readTimeCoverage(Element tElem) {
if (tElem == null) return null;
Calendar calendar = readCalendar(tElem.getAttributeValue("calendar"));
DateType start = readDate(tElem.getChild("start", Catalog.defNS), calendar);
DateType end = readDate(tElem.getChild("end", Catalog.defNS), calendar);
TimeDuration duration = readDuration(tElem.getChild("duration", Catalog.defNS));
TimeDuration resolution = readDuration(tElem.getChild("resolution", Catalog.defNS));
try {
return new DateRange(start, end, duration, resolution);
} catch (java.lang.IllegalArgumentException e) {
errlog.format(" ** warning: TimeCoverage error ='%s'%n", e.getMessage());
return null;
}
}
protected Calendar readCalendar(String calendarAttribValue) {
if (calendarAttribValue == null) {
return Calendar.getDefault();
}
Calendar calendar = Calendar.get(calendarAttribValue);
if (calendar == null) {
errlog.format(" ** Parse error: Bad calendar name = '%s'%n", calendarAttribValue);
return Calendar.getDefault();
}
return calendar;
}
protected DateType readDate(Element elem, Calendar calendar) {
if (elem == null) return null;
String format = elem.getAttributeValue("format");
String type = elem.getAttributeValue("type");
return makeDateType(elem.getText(), format, type, calendar);
}
protected DateType makeDateType(String text, String format, String type, Calendar calendar) {
if (text == null) return null;
try {
return new DateType(text, format, type, calendar);
} catch (java.text.ParseException e) {
errlog.format(" ** Parse error: Bad date format = '%s'%n", text);
return null;
}
}
protected TimeDuration readDuration(Element elem) {
if (elem == null) return null;
String text = null;
try {
text = elem.getText();
return new TimeDuration(text);
} catch (java.text.ParseException e) {
errlog.format(" ** Parse error: Bad duration format = '%s'%n", text);
return null;
}
}
/*
*/
protected ThreddsMetadata.VariableGroup readVariables( Element varsElem) {
if (varsElem == null) return null;
String vocab = varsElem.getAttributeValue("vocabulary");
ThreddsMetadata.UriResolved variableVocabUri = readUri(varsElem, "Variables vocabulary");
java.util.List vlist = varsElem.getChildren("variable", Catalog.defNS);
ThreddsMetadata.UriResolved variableMap = readUri(varsElem.getChild("variableMap", Catalog.defNS), "Variables Map");
if ((variableMap != null) && vlist.size() > 0) { // cant do both
errlog.format(" ** Catalog error: cant have variableMap and variable in same element '%s'%n", varsElem);
}
List variables = new ArrayList<>();
for (Element e : vlist) {
variables.add(readVariable(e));
}
return new ThreddsMetadata.VariableGroup(vocab, variableVocabUri, variableMap, variables);
}
static public ThreddsMetadata.Variable readVariable(Element varElem) {
if (varElem == null) return null;
String name = varElem.getAttributeValue("name");
String desc = varElem.getText();
String vocabulary_name = varElem.getAttributeValue("vocabulary_name");
String units = varElem.getAttributeValue("units");
String id = varElem.getAttributeValue("vocabulary_id");
return new ThreddsMetadata.Variable(name, desc, vocabulary_name, units, id);
}
protected ThreddsMetadata.Vocab readControlledVocabulary(Element elem) {
if (elem == null) return null;
return new ThreddsMetadata.Vocab(elem.getText(), elem.getAttributeValue("vocabulary"));
}
private ThreddsMetadata.UriResolved readUri(Element elemWithHref, String what) {
if (elemWithHref == null) return null;
String mapHref = elemWithHref.getAttributeValue("href", Catalog.xlinkNS);
if (mapHref == null) return null;
try {
String mapUri = URLnaming.resolve(baseURI.toString(), mapHref);
return new ThreddsMetadata.UriResolved(mapHref, new URI(mapUri));
} catch (Exception e) {
errlog.format(" ** Invalid %s URI= '%s' err='%s'%n", what, mapHref, e.getMessage());
return null;
}
}
}