/* * Copyright 1998-2015 University Corporation for Atmospheric Research/Unidata * * Portions of this software were developed by the Unidata Program at the * University Corporation for Atmospheric Research. * * Access and use of this software shall impose the following obligations * and understandings on the user. The user is granted the right, without * any fee or cost, to use, copy, modify, alter, enhance and distribute * this software, and any derivative works thereof, and its supporting * documentation for any purpose whatsoever, provided that this entire * notice appears in all copies of the software, derivative works and * supporting documentation. Further, UCAR requests that the user credit * UCAR/Unidata in any publications that result from the use of this * software or in any product that includes this software. The names UCAR * and/or Unidata, however, may not be used in any advertising or publicity * to endorse or promote any products or commercial entity unless specific * written permission is obtained from UCAR/Unidata. The user also * understands that UCAR/Unidata is not obligated to provide the user with * any support, consulting, training or assistance of any kind with regard * to the use, operation and performance of this software nor to provide * the user with any updates, revisions, new versions or "bug fixes." * * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. */ package thredds.client.catalog; import net.jcip.annotations.Immutable; import org.jdom2.Namespace; import thredds.client.catalog.builder.AccessBuilder; import thredds.client.catalog.builder.CatalogBuilder; import thredds.client.catalog.builder.CatalogRefBuilder; import thredds.client.catalog.builder.DatasetBuilder; import ucar.nc2.time.CalendarDate; import ucar.nc2.util.URLnaming; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * A Client Catalog * * @author caron * @since 1/7/2015 */ @Immutable public class Catalog extends DatasetNode { static public final String CATALOG_NAMESPACE_10 = "http://www.unidata.ucar.edu/namespaces/thredds/InvCatalog/v1.0"; static public final Namespace defNS = Namespace.getNamespace(CATALOG_NAMESPACE_10); static public final String NJ22_NAMESPACE = "http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2"; static public final String NJ22_NAMESPACE_HTTPS = "https://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2"; static public final Namespace ncmlNS = Namespace.getNamespace("ncml", NJ22_NAMESPACE); static public final Namespace ncmlNSHttps = Namespace.getNamespace("ncml", NJ22_NAMESPACE_HTTPS); static public final String XLINK_NAMESPACE = "http://www.w3.org/1999/xlink"; static public final Namespace xlinkNS = Namespace.getNamespace("xlink", XLINK_NAMESPACE); static public final Namespace xsiNS = Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); ////////////////////////////////////////////////////////////////////////////////////////// private final URI baseURI; public Catalog(URI baseURI, String name, Map<String, Object> flds, List<DatasetBuilder> datasets) { super(null, name, flds, datasets); this.baseURI = baseURI; Map<String, Dataset> datasetMap = new HashMap<>(); addDatasetsToHash(getDatasets(), datasetMap); if (!datasetMap.isEmpty()) flds.put(Dataset.DatasetHash, datasetMap); } private void addDatasetsToHash(List<Dataset> datasets, Map<String, Dataset> datasetMap) { if (datasets == null) return; for (Dataset ds : datasets) { String id = ds.getId(); if (id != null) datasetMap.put(id, ds); addDatasetsToHash(ds.getDatasets(), datasetMap); } } public URI getBaseURI() { return baseURI; } public CalendarDate getExpires() { return (CalendarDate) flds.get(Dataset.Expires); } public String getVersion() { return (String) flds.get(Dataset.Version); } public List<Service> getServices() { List<Service> services = (List<Service>) flds.get(Dataset.Services); return services == null ? new ArrayList<Service>(0) : services; } public Service findService(String serviceName) { if (serviceName == null) return null; List<Service> services = (List<Service>) flds.get(Dataset.Services); return findService(services, serviceName); } private Service findService(List<Service> services, String want) { if (services == null) return null; for (Service s : services) { if (s.getName().equals(want)) return s; } for (Service s : services) { Service result = findService(s.getNestedServices(), want); if (result != null) return result; } return null; } public List<Property> getProperties() { List<Property> properties = (List<Property>) flds.get(Dataset.Properties); return properties == null ? new ArrayList<Property>(0) : properties; } public Dataset findDatasetByID( String id) { Map<String, Dataset> datasetMap = (Map<String, Dataset>) flds.get(Dataset.DatasetHash); return datasetMap == null ? null : datasetMap.get(id); } /** * Resolve reletive URIs, using the catalog's base URI. If the uriString is not reletive, then * no resolution is done. This also allows baseURI to be a file: scheme. * * @param uriString any url, reletive or absolute * @return resolved url string, or null on error * @throws java.net.URISyntaxException if uriString violates RFC 2396 * @see java.net.URI#resolve */ public URI resolveUri(String uriString) throws URISyntaxException { if (baseURI == null) return new URI(uriString); String resolved = URLnaming.resolve(baseURI.toString(), uriString); return new URI(resolved); } // look is this different than URLnaming ?? public static URI resolveUri(URI baseURI, String uriString) throws URISyntaxException { URI want = new URI(uriString); if ((baseURI == null) || want.isAbsolute()) return want; // gotta deal with file ourself String scheme = baseURI.getScheme(); if ((scheme != null) && scheme.equals("file")) { String baseString = baseURI.toString(); if ((uriString.length() > 0) && (uriString.charAt(0) == '#')) return new URI(baseString + uriString); int pos = baseString.lastIndexOf('/'); if (pos > 0) { String r = baseString.substring(0, pos + 1) + uriString; return new URI(r); } } //otherwise let the URI class resolve it return baseURI.resolve(want); } public String getUriString() { URI baseURI = getBaseURI(); return baseURI == null ? null : baseURI.toString(); } ////////////////////////////////////////////////////////////////////////////////// // from DeepCopyUtils public Catalog subsetCatalogOnDataset( Dataset dataset) { if ( dataset == null ) throw new IllegalArgumentException( "Dataset may not be null." ); if ( dataset.getParentCatalog() != this ) throw new IllegalArgumentException( "Catalog must contain the dataset." ); CatalogBuilder builder = new CatalogBuilder(); URI docBaseUri = formDocBaseUriForSubsetCatalog( dataset ); builder.setBaseURI(docBaseUri); builder.setName( dataset.getName()); List<Service> neededServices = new ArrayList<>(); DatasetBuilder topDs = copyDataset( null, dataset, neededServices, true ); // LOOK, cant set catalog as datasetNode parent for (Service s : neededServices) builder.addService(s); builder.addDataset( topDs ); return builder.makeCatalog(); } private DatasetBuilder copyDataset( DatasetBuilder parent, Dataset dataset, List<Service> neededServices, boolean copyInherited ) { neededServices.add(dataset.getServiceDefault()); DatasetBuilder result; if ( dataset instanceof CatalogRef ) { CatalogRef catRef = (CatalogRef) dataset; CatalogRefBuilder catBuilder = new CatalogRefBuilder( parent); catBuilder.setHref( catRef.getXlinkHref()); catBuilder.setTitle( catRef.getName()); result = catBuilder; } else { result = new DatasetBuilder(parent); List<Access> access = dataset.getLocalFieldAsList(Dataset.Access); // dont expand for ( Access curAccess : access) { result.addAccess( copyAccess( result, curAccess, neededServices )); } List<Dataset> datasets = dataset.getLocalFieldAsList(Dataset.Datasets); // dont expand for ( Dataset currDs : datasets) { result.addDataset( copyDataset( result, currDs, neededServices, copyInherited )); } } result.setName( dataset.getName() ); result.transferMetadata( dataset, false ); return result; } private AccessBuilder copyAccess( DatasetBuilder parent, Access access, List<Service> neededServices ) { neededServices.add(access.getService()); // LOOK may get dups return new AccessBuilder( parent, access.getUrlPath(), access.getService(), access.getDataFormatName(), access.getDataSize() ); } private URI formDocBaseUriForSubsetCatalog( Dataset dataset ) { String catDocBaseUri = getUriString(); String subsetDocBaseUriString = catDocBaseUri + "/" + ( dataset.getID() != null ? dataset.getID() : dataset.getName() ); try { return new URI( subsetDocBaseUriString); } catch ( URISyntaxException e ) { // This shouldn't happen. But just in case ... throw new IllegalStateException( "Bad document Base URI for new catalog [" + catDocBaseUri + "/" + (dataset.getID() != null ? dataset.getID() : dataset.getName()) + "].", e ); } } }