/* * Copyright 1998-2009 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.catalog; import ucar.nc2.time.CalendarDateRange; import ucar.nc2.units.DateType; import java.net.URI; import ucar.nc2.constants.FeatureType; /** * A reference to a InvCatalog. The referenced catalog is not read until getDatasets() is called. * A client will see the referenced catalog as a nested dataset. * <p/> * <p/> * The client can also do asynchronous reading, if the InvCatalogFactory supports it, and if readAsynch() is used. * <p/> * <pre> * Parent relationship: * ds -> catRef -- catalog * ^ top -> ds ... * <p/> * ParentView relationship: * ds -> catRef -> top -> ds ... * (or) -> ds if UseProxy * <p/> * </pre> * * @author john caron * @see InvDatasetImpl for API, thredds.catalog.ui.CatalogTreeView as example to read asynchronously */ public class InvCatalogRef extends InvDatasetImpl { private String href; private Boolean useRemoteCatalogService; private InvDatasetImpl proxy = null; // top dataset of referenced catalog private URI uri = null; private String errMessage = null; private boolean init = false, useProxy = false; private boolean debug = false, debugProxy = false, debugAsynch = false; /** * Constructor. * * @param parent : parent dataset * @param name : display name of collection * @param href : URL to another catalog */ public InvCatalogRef(InvDatasetImpl parent, String name, String href) { super(parent, name); setXlinkHref(href); } /** * Constructor. * * @param parent : parent dataset * @param name : display name of collection * @param href : URL to another catalog * @param useRemoteCatalogService : force catalogRef to go through the remoteCatalogService */ public InvCatalogRef(InvDatasetImpl parent, String name, String href, Boolean useRemoteCatalogService) { this(parent, name, href); this.useRemoteCatalogService = useRemoteCatalogService; } public Boolean useRemoteCatalogService() { return useRemoteCatalogService; } /** * @return Xlink Href, as a String, unresolved */ public String getXlinkHref() { return href; } public void setXlinkHref(String href) { this.href = href.trim(); this.uri = null; } /** * @return Xlink reference as a URI, resolved */ public URI getURI() { if (uri != null) return uri; // may be relative try { return getParentCatalog().resolveUri(href); } catch (java.net.URISyntaxException e) { synchronized (this) { errMessage = "URISyntaxException on url " + href + " = " + e.getMessage(); } return null; } } /** * Get a list of all the nested datasets. * @return Datasets. This triggers a read of the referenced catalog the first time its called. */ @Override public java.util.List<InvDataset> getDatasets() { read(); return useProxy ? proxy.getDatasets() : super.getDatasets(); } /** * @return true the referenced catalog has been read */ public boolean isRead() { return init; } /** * This triggers a read of the referenced catalog the first time its called. * @return top dataset of referenced catalog. */ public InvDatasetImpl getProxyDataset() { read(); return proxy; } /** * Release resources - undo the read of the catalog. This is needed when crawling large catalogs. * For modest catalogs that you will repeatedly examine, do not use this method. */ public void release() { datasets = new java.util.ArrayList<>(); proxy = null; useProxy = false; init = false; } ////////////////////////////////////////////// @Override public boolean finish() { return super.finish(); } private synchronized void read() { if (init) return; URI uriResolved = getURI(); if (uriResolved == null) { // this is to display an error message proxy = new InvDatasetImpl(null, "HREF ERROR"); proxy.addProperty(new InvProperty("HREF ERROR", errMessage)); datasets.add(proxy); init = true; return; } // open and read the referenced catalog XML try { if (debug) System.out.println(" InvCatalogRef read " + getFullName() + " hrefResolved = " + uriResolved); InvCatalogFactory factory = InvCatalogFactory.getDefaultFactory(true); // InvCatalogFactory factory = ((InvCatalogImpl) getParentCatalog()).getCatalogFactory(); InvCatalogImpl cat = factory.readXML(uriResolved.toString()); finishCatalog(cat); } catch (Exception e) { // this is to display an error message proxy = new InvDatasetImpl(null, "HREF ERROR"); if (debug) System.out.println("HREF ERROR =\n " + href + " err= " + e.getMessage()); proxy.addProperty(new InvProperty("HREF ERROR", href)); datasets.add(proxy); init = true; } } private void finishCatalog(InvCatalogImpl cat) { InvCatalogImpl parentCatalog; if (cat.hasFatalError()) { // this is to display an error message proxy = new InvDatasetImpl(null, "ERROR OPENING"); StringBuilder out = new StringBuilder(); cat.check(out); if (debug) System.out.println("PARSE ERROR =\n " + out.toString()); proxy.addProperty(new InvProperty("ERROR OPENING", out.toString())); proxy.finish(); } else { // check for filter parentCatalog = (InvCatalogImpl) getParentCatalog(); DatasetFilter filter = parentCatalog.getDatasetFilter(); if (filter != null) cat.filter(filter); proxy = cat.getDataset(); if (proxy.getMark()) { proxy.setName(proxy.getName() + " (EMPTY)"); proxy.addProperty(new InvProperty("isEmpty", "true")); proxy.finish(); } String name = getName().trim(); String proxyName = proxy.getName().trim(); useProxy = proxyName.equals(name) && !(proxy instanceof InvCatalogRef); if (debugProxy) System.out.println("catRefname=" + name + "=topName=" + proxyName + "=" + useProxy); //if (useProxy) { //for (int i=0; i<docs.size(); i++) // proxy.addDocumentation( (InvDocumentation) docs.get(i)); //proxy.finish(); /*List proxyData = proxy.getDatasets(); for (int i=0; i<proxyData.size(); i++) { InvDatasetImpl ds = (InvDatasetImpl) proxyData.get(i); ds.setViewParent( this); // for treeview } */ //} } // make proxy the top dataset datasets.add(proxy); //proxy.setViewParent( this); // for treeview /* all this rigamorole to send an event when reference is read. if (parentCatalog != null) { // get topmost catalog InvCatalogImpl top = parentCatalog.getTopCatalog(); cat.setTopCatalog(top); // may not be on the awt event thread, so need to do invokeLater javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { InvCatalogImpl top = ((InvCatalogImpl)getParentCatalog()).getTopCatalog(); top.firePropertyChangeEvent( new java.beans.PropertyChangeEvent(top, "InvCatalogRefInit", null, this)); } }); } */ init = true; } /** * Read the referenced catalog asynchronously, if the catalog factory supports it. * If it doesnt, this method will work equivilently to read(), which is called the first time * getDatasets() is called. If the catalog is already read in, the callback will be called * immediately, before this method exits. * * @param factory : use this catalog factory * @param caller when catalog is read * @see CatalogSetCallback */ public synchronized void readAsynch(InvCatalogFactory factory, CatalogSetCallback caller) { if (init) { caller.setCatalog((InvCatalogImpl) getParentCatalog()); return; } // may be reletive String hrefResolved; try { java.net.URI uri = getParentCatalog().resolveUri(href); hrefResolved = uri.toString(); } catch (java.net.URISyntaxException e) { // this is to display an error message proxy = new InvDatasetImpl(null, "HREF ERROR"); if (debug) System.out.println("HREF ERROR =\n " + href + " err= " + e.getMessage()); proxy.addProperty(new InvProperty("HREF ERROR", href)); datasets.add(proxy); return; } // open and read the referenced catalog XML asynchronously // setCatalog will be called when ready try { if (debug) System.out.println(" InvCatalogRef readXMLasynch " + getFullName() + " hrefResolved = " + hrefResolved); factory.readXMLasynch(hrefResolved, new Callback(caller)); } catch (Exception e) { // this is to display an error message proxy = new InvDatasetImpl(null, "HREF ERROR"); if (debug) System.out.println("HREF ERROR =\n " + href + " err= " + e.getMessage()); proxy.addProperty(new InvProperty("HREF ERROR", href)); datasets.add(proxy); } } private class Callback implements CatalogSetCallback { CatalogSetCallback caller; Callback(CatalogSetCallback caller) { this.caller = caller; } public void setCatalog(InvCatalogImpl cat) { if (debugAsynch) System.out.println(" setCatalog was called"); finishCatalog(cat); caller.setCatalog(cat); } public void failed() { if (debugAsynch) System.out.println(" setCatalog failed"); caller.failed(); } } boolean check(StringBuilder out, boolean show) { return isRead() ? proxy.check(out, show) : super.check(out, show); } /* public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof InvCatalogRef)) return false; InvCatalogRef invCatalogRef = (InvCatalogRef) o; if (href != null ? !href.equals(invCatalogRef.href) : invCatalogRef.href != null) return false; // Add the name comparison from super. if (name != null ? !name.equals(invCatalogRef.name) : invCatalogRef.name != null) return false; return true; } public int hashCode() { //int result = super.hashCode(); int result = 17; result = 29 * result + (href != null ? href.hashCode() : 0); result = 29 * result + (name != null ? name.hashCode() : 0); return result; } */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; // if (!super.equals(o)) return false; InvCatalogRef that = (InvCatalogRef) o; if (href != null ? !href.equals(that.href) : that.href != null) return false; if (name != null ? !name.equals(that.name) : that.name != null) return false; return true; } @Override public int hashCode() { int result = 17; result = 31 * result + (href != null ? href.hashCode() : 0); result = 29 * result + (name != null ? name.hashCode() : 0); return result; } //// proxy @Override public thredds.catalog.InvDatasetImpl findDatasetByName(java.lang.String p0) { return !useProxy ? super.findDatasetByName(p0) : proxy.findDatasetByName(p0); } @Override public java.lang.String findProperty(java.lang.String p0) { return !useProxy ? super.findProperty(p0) : proxy.findProperty(p0); } @Override public thredds.catalog.InvService findService(java.lang.String p0) { return !useProxy ? super.findService(p0) : proxy.findService(p0); } @Override public thredds.catalog.InvAccess getAccess(thredds.catalog.ServiceType p0) { return !useProxy ? super.getAccess(p0) : proxy.getAccess(p0); } @Override public java.util.List<InvAccess> getAccess() { return !useProxy ? super.getAccess() : proxy.getAccess(); } @Override public java.lang.String getAlias() { return !useProxy ? super.getAlias() : proxy.getAlias(); } @Override public String getAuthority() { return useProxy ? proxy.getAuthority() : super.getAuthority(); } @Override public thredds.catalog.CollectionType getCollectionType() { return !useProxy ? super.getCollectionType() : proxy.getCollectionType(); } @Override public java.util.List<ThreddsMetadata.Contributor> getContributors() { return !useProxy ? super.getContributors() : proxy.getContributors(); } @Override public java.util.List<ThreddsMetadata.Source> getCreators() { return !useProxy ? super.getCreators() : proxy.getCreators(); } @Override public thredds.catalog.DataFormatType getDataFormatType() { return !useProxy ? super.getDataFormatType() : proxy.getDataFormatType(); } @Override public FeatureType getDataType() { return !useProxy ? super.getDataType() : proxy.getDataType(); } @Override public java.util.List<DateType> getDates() { return !useProxy ? super.getDates() : proxy.getDates(); } @Override public java.util.List<InvDocumentation> getDocumentation() { return !useProxy ? super.getDocumentation() : proxy.getDocumentation(); } @Override public java.lang.String getDocumentation(java.lang.String p0) { return !useProxy ? super.getDocumentation(p0) : proxy.getDocumentation(p0); } @Override public java.lang.String getFullName() { return !useProxy ? super.getFullName() : proxy.getFullName(); } @Override public thredds.catalog.ThreddsMetadata.GeospatialCoverage getGeospatialCoverage() { return !useProxy ? super.getGeospatialCoverage() : proxy.getGeospatialCoverage(); } @Override public java.lang.String getID() { return !useProxy ? super.getID() : proxy.getID(); } @Override public java.util.List<ThreddsMetadata.Vocab> getKeywords() { return !useProxy ? super.getKeywords() : proxy.getKeywords(); } @Override protected boolean getMark() { return !useProxy ? super.getMark() : proxy.getMark(); } @Override public java.util.List<InvMetadata> getMetadata(thredds.catalog.MetadataType p0) { return !useProxy ? super.getMetadata(p0) : proxy.getMetadata(p0); } @Override public java.util.List<InvMetadata> getMetadata() { return !useProxy ? super.getMetadata() : proxy.getMetadata(); } @Override public java.lang.String getName() { if (!useProxy) return super.getName(); return (proxy == null) ? "N/A" : proxy.getName(); } @Override public thredds.catalog.InvDataset getParent() { return !useProxy ? super.getParent() : proxy.getParent(); } /* LOOK public thredds.catalog.InvCatalog getParentCatalog() { return !useProxy ? super.getParentCatalog() : proxy.getParentCatalog(); } */ @Override public java.util.List<ThreddsMetadata.Vocab> getProjects() { return !useProxy ? super.getProjects() : proxy.getProjects(); } @Override public java.util.List<InvProperty> getProperties() { return !useProxy ? super.getProperties() : proxy.getProperties(); } @Override public java.util.List<ThreddsMetadata.Source> getPublishers() { return !useProxy ? super.getPublishers() : proxy.getPublishers(); } @Override public thredds.catalog.InvService getServiceDefault() { return !useProxy ? super.getServiceDefault() : proxy.getServiceDefault(); } @Override public CalendarDateRange getCalendarDateCoverage() { return !useProxy ? super.getCalendarDateCoverage() : proxy.getCalendarDateCoverage(); } @Override public java.lang.String getUniqueID() { return !useProxy ? super.getUniqueID() : proxy.getUniqueID(); } @Override public java.lang.String getUrlPath() { return !useProxy ? super.getUrlPath() : proxy.getUrlPath(); } @Override public java.lang.Object getUserProperty(java.lang.Object p0) { return !useProxy ? super.getUserProperty(p0) : proxy.getUserProperty(p0); } @Override public java.util.List<ThreddsMetadata.Variables> getVariables() { return !useProxy ? super.getVariables() : proxy.getVariables(); } @Override public String getVariableMapLink() { return !useProxy ? super.getVariableMapLink() : proxy.getVariableMapLink(); } @Override public boolean hasAccess() { return !useProxy ? super.hasAccess() : proxy.hasAccess(); } @Override public boolean hasNestedDatasets() { return useProxy ? proxy.hasNestedDatasets() : true; } @Override public boolean isHarvest() { return !useProxy ? super.isHarvest() : proxy.isHarvest(); } }