thredds.catalog.InvCatalogImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cdm Show documentation
Show all versions of cdm Show documentation
The NetCDF-Java Library is a Java interface to NetCDF files,
as well as to many other types of scientific data formats.
The newest version!
/*
* 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.units.DateType;
import java.util.*;
import java.net.*;
/**
* Concrete implementation of a Thredds catalog object. Use this when you are constructing or modifying.
*
* @author john caron
* @see InvCatalog
*/
public class InvCatalogImpl extends InvCatalog {
private String createFrom;
private List roots = new ArrayList();
// validation
private StringBuilder log = new StringBuilder();
private boolean hasError = false;
private boolean isStatic = false;
/**
* Munge this catalog so the given dataset is the top catalog.
*
* @param ds make this top; must be existing dataset in this catalog.
*
* @deprecated in favor of thredds.catalog.util.DeepCopyUtils.subsetCatalogOnDataset
*/
public void subset(InvDataset ds) {
InvDatasetImpl dataset = (InvDatasetImpl) ds;
// Make all inherited metadata local.
dataset.transferMetadata(dataset, true);
topDataset = dataset;
datasets.clear(); // throw away the rest
datasets.add(topDataset);
// parent lookups need to be local
//InvService service = dataset.getServiceDefault();
//if (service != null) LOOK
// dataset.serviceName = service.getName();
dataset.dataType = dataset.getDataType();
// all properties need to be local
// LOOK dataset.setPropertiesLocal( new ArrayList(dataset.getProperties()));
// next part requires this before it
dataset.setCatalog(this);
dataset.parent = null;
// any referenced services need to be local
List services = new ArrayList(dataset.getServicesLocal());
findServices(services, dataset);
dataset.setServicesLocal(services);
finish();
}
// /**
// * Return a catalog with a shallow copy of the given dataset as the top dataset.
// *
// * @param dataset the dataset to copy into a new catalog
// * @return A new catalog with a shallow copy of the given dataset as the top dataset.
// */
// public InvCatalogImpl subsetShallowCopy( InvDatasetImpl dataset)
// {
// InvCatalogImpl newCatalog = new InvCatalogImpl( null, "1.0.2", null);
// InvDatasetImpl newTopDataset = new InvDatasetImpl( null, dataset.getName());
// newTopDataset.transferMetadata( dataset );
// if (dataset.getAccess() != null && ! dataset.getAccess().isEmpty())
// {
// newTopDataset.addAccess( dataset.getAccess() );
// }
// dataset.getDatasets();
// dataset.getID();
// dataset.getServiceName();
// dataset.getServicesLocal();
// dataset.getUrlPath();
//
// // ToDo LOOK Probably fine for throw away catalog that is only generated so that XML can be generated.
// dataset.finish();
//
// newCatalog.addDataset( newTopDataset );
//
// return newCatalog;
// }
void findServices(List result, InvDataset ds) {
if (ds instanceof InvCatalogRef) return;
// look for access elements with unresolved services
for (InvAccess a : ds.getAccess()) {
InvService s = a.getService();
InvDataset d = a.getDataset();
if (null == d.findService(s.getName()) && !(result.contains(s)))
result.add(s);
}
// recurse into nested datasets
for (InvDataset nested : ds.getDatasets()) {
findServices(result, nested);
}
}
/**
* Munge this catalog to remove any dataset that doesnt pass through the filter.
*
* @param filter remove datasets that dont pass this filter.
*/
public void filter(DatasetFilter filter) {
mark(filter, topDataset);
delete(topDataset);
this.filter = filter;
}
// got to keep it for catalogRef's
private DatasetFilter filter = null;
protected DatasetFilter getDatasetFilter() {
return filter;
}
// see if this ds should be filtered out.
// if so, setMark and return true.
// if any nested dataset are kept, then keep it.
// unread CatalogRefs are always kept.
private boolean mark(DatasetFilter filter, InvDatasetImpl ds) {
if (ds instanceof InvCatalogRef) {
InvCatalogRef catRef = (InvCatalogRef) ds;
if (!catRef.isRead()) return false;
}
// recurse into nested datasets first
boolean allMarked = true;
for (InvDataset nested : ds.getDatasets()) {
allMarked &= mark(filter, (InvDatasetImpl) nested);
}
if (!allMarked) return false;
if (filter.accept(ds) >= 0)
return false;
// mark for deletion
ds.setMark(true);
if (debugFilter) System.out.println(" mark " + ds.getName());
return true;
}
private boolean debugFilter = false;
// remove marked datasets
private void delete(InvDatasetImpl ds) {
if (ds instanceof InvCatalogRef) {
InvCatalogRef catRef = (InvCatalogRef) ds;
if (!catRef.isRead()) return;
}
Iterator iter = ds.getDatasets().iterator();
while (iter.hasNext()) {
InvDatasetImpl nested = (InvDatasetImpl) iter.next();
if (nested.getMark()) {
iter.remove();
if (debugFilter) System.out.println(" remove " + nested.getName());
} else
delete(nested);
}
}
///////////////////////////////////////////////////////////////////////////////
// construction and internals
/**
* Construct an InvCatalog.
* You must call finish() after all objects are added.
*
* @param name : catalog name.
* @param version : catalog version.
* @param baseURI : catalog base URI (external).
*/
public InvCatalogImpl(String name, String version, URI baseURI) {
this(name, version, null, baseURI);
}
/**
* Construct an InvCatalog.
* You must call finish() after all objects are added.
*
* @param name : catalog name.
* @param version : catalog version.
* @param expires : date/time catalog expires.
* @param baseURI : catalog base URI (external).
*/
public InvCatalogImpl(String name, String version, DateType expires, URI baseURI) {
this.name = name;
this.version = version;
this.expires = expires;
this.baseURI = baseURI;
}
/**
* Finish constructing after all elements have been added or modified.
* This routine will do any needed internal consistency work.
* Its ok to call multiple times.
*
* @return true if successful.
*/
public boolean finish() {
// make topDataset if needed
//if (topDataset == null) {
if (datasets.size() == 1) { // already only one; make it top
topDataset = (InvDatasetImpl) datasets.get(0);
} else { // create one
topDataset = new InvDatasetImpl(null, name == null ? "Top Dataset" : name);
for (InvDataset dataset : datasets)
topDataset.addDataset((InvDatasetImpl) dataset);
topDataset.setServicesLocal(services);
}
//}
topDataset.setCatalog(this);
// build dataset hash table
dsHash = new HashMap();
addDatasetIds(topDataset);
// recurse through the datasets and finish them
return topDataset.finish();
}
private void addDatasetIds(InvDatasetImpl ds) {
addDatasetByID(ds);
if (ds instanceof InvCatalogRef) return;
//if (ds instanceof InvDatasetFmrc) return;
// recurse into nested
for (InvDataset invDataset : ds.getDatasets()) {
InvDatasetImpl nested = (InvDatasetImpl) invDataset;
addDatasetIds(nested);
}
}
/**
* Add Dataset to internal hash.
*
* @param ds : add this dataset if ds.getID() != null
* @see InvCatalog#findDatasetByID
*/
public void addDatasetByID(InvDatasetImpl ds) {
if (ds.getID() != null)
dsHash.put(ds.getID(), ds);
}
/**
* Find the dataset in this catalog by its ID. If found, remove it.
*
* @param ds Remove this dataset from the hash
*/
public void removeDatasetByID(InvDatasetImpl ds) {
if (ds.getID() != null)
dsHash.remove(ds.getID());
}
/**
* Add Dataset (1.0)
*
* @param ds add this dataset
*/
public void addDataset(InvDatasetImpl ds) {
if (ds != null)
datasets.add(ds);
}
/**
* Remove the given dataset from this catalog if it is a direct child of this catalog.
*
* @param ds remove this dataset
* @return true if found and removed
*/
public boolean removeDataset(InvDatasetImpl ds) {
if (this.datasets.remove(ds)) {
ds.setParent(null);
removeDatasetByID(ds);
return true;
}
return false;
}
/**
* Replace the given dataset if it is a nested dataset.
*
* @param remove - the dataset element to be removed
* @param add - the dataset element to be added
* @return true on success
*/
public boolean replaceDataset(InvDatasetImpl remove, InvDatasetImpl add) {
if (topDataset.equals(remove)) {
topDataset = add;
topDataset.setCatalog(this);
}
for (int i = 0; i < datasets.size(); i++) {
InvDataset dataset = datasets.get(i);
if (dataset.equals(remove)) {
datasets.set(i, add);
removeDatasetByID(remove);
addDatasetByID(add);
return true;
}
}
return false;
}
/**
* Add Property (1.0)
*
* @param p add this property
*/
public void addProperty(InvProperty p) {
properties.add(p);
}
/**
* Add Service (1.0)
*
* @param s add this service
*/
public void addService(InvService s) {
if (s == null)
throw new IllegalArgumentException("Service to add was null.");
// While adding a service, there are three possible results:
if (s.getName() != null) {
Object obj = serviceHash.get(s.getName());
if (obj == null) {
// 1) No service with matching name entry was found, add given service;
serviceHash.put(s.getName(), s);
services.add(s);
return;
} else {
// A service with matching name was found.
if (s.equals(obj)) {
// 2) matching name entry, objects are equal so OK;
return;
} else {
// 3) matching name entry, objects are not equal so ???
// @todo throw an exception???
// Currently just dropping given service
log.append("Multiple Services with the same name\n");
return;
}
}
}
}
/**
* Add top-level InvDataset to this catalog.
*
* @deprecated Use addDataset() instead; datamodel now allows multiple top level datasets.
*/
public void setDataset(InvDatasetImpl ds) {
topDataset = ds;
addDataset(ds);
}
/**
* String describing how the catalog was created, for debugging.
*
* @return how the catalog was created, for debugging
*/
public String getCreateFrom() {
return createFrom;
}
/**
* Set how the catalog was created, for debugging.
* @param createFrom how the catalog was created, for debugging
*/
public void setCreateFrom(String createFrom) {
this.createFrom = createFrom;
}
/**
* Set the catalog base URI.
* Its used to resolve reletive URLS.
* @param baseURI set to this
*/
public void setBaseURI(URI baseURI) {
this.baseURI = baseURI;
}
/**
* @return the catalog base URI.
*/
public URI getBaseURI() {
return baseURI;
}
/*
* @return DTD string
*
public String getDTDid() {
return dtdID;
}
/*
* set DTD
*
public void setDTDid(String dtdID) {
this.dtdID = dtdID;
} */
/**
* Set the expires date after which the catalog is no longer valid.
*
* @param expiresDate a {@link DateType} representing the date after which the catlog is no longer valid.
*/
public void setExpires(DateType expiresDate) {
this.expires = expiresDate;
}
/**
* Check if there is a fatal error and catalog should not be used.
*
* @return true if catalog not useable.
*/
public boolean hasFatalError() {
return hasError;
}
/**
* Append an error message to the message log. Call check() to get the log when
* everything is done.
*
* @param message append this message to log
* @param isInvalid true if this is a fatal error.
*/
public void appendErrorMessage(String message, boolean isInvalid) {
log.append(message);
hasError = hasError | isInvalid;
}
/**
* Check internal data structures.
*
* @param out : print errors here
* @param show : print messages for each object (debug)
* @return true if no fatal consistency errors.
*/
public boolean check(StringBuilder out, boolean show) {
boolean isValid = !hasError;
out.append("----Catalog Validation\n");
if (log.length() > 0)
out.append(log);
if (show) System.out.println(" catalog valid = " + isValid);
//if (topDataset != null)
// isValid &= topDataset.check( out, show);
for (InvDataset ds : datasets) {
InvDatasetImpl dsi = (InvDatasetImpl) ds;
dsi.check(out, show); // cant make it invalid !!
}
return isValid;
}
public String getLog() {
return log.toString();
}
/**
* Debugging: dump entire data structure.
*
* @return String representation.
*/
public String dump() {
StringBuilder buff = new StringBuilder(1000);
buff.setLength(0);
buff.append("Catalog <").append(getName())
.append("> <").append(getVersion())
.append("> <").append(getCreateFrom()).append(">\n");
buff.append(topDataset.dump(2));
return buff.toString();
}
/*
* Add a PropertyChangeEvent Listener. THIS IS EXPERIMENTAL DO NOT RELY ON.
* Throws a PropertyChangeEvent:
* - propertyName = "InvCatalogRefInit", getNewValue() = InvCatalogRef that was just initialized
*
* @param l the listener
*
public void addPropertyChangeListener(PropertyChangeListener l) {
if (listenerList == null) listenerList = new EventListenerList();
listenerList.add(PropertyChangeListener.class, l);
}
/**
* Remove a PropertyChangeEvent Listener.
* @param l the listener
*
public void removePropertyChangeListener(PropertyChangeListener l) {
listenerList.remove(PropertyChangeListener.class, l);
}
private EventListenerList listenerList = null;
// PropertyChangeEvent(Object source, String propertyName, Object oldValue, Object newValue)
void firePropertyChangeEvent(PropertyChangeEvent event) {
// System.out.println("firePropertyChangeEvent "+event);
if (listenerList == null) return;
// Process the listeners last to first
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == PropertyChangeListener.class) {
((PropertyChangeListener) listeners[i + 1]).propertyChange(event);
}
}
} */
/**
* This finds the topmost catalog, even when its a InvCatalogRef.
* Used to throw a PropertyChange event on the top catalog.
*
* @return top catalog
*/
InvCatalogImpl getTopCatalog() {
return (top == null) ? this : top;
}
void setTopCatalog(InvCatalogImpl top) {
this.top = top;
}
private InvCatalogImpl top = null;
////////////////////////////////////////////
/* private InvCatalogFactory factory = null;
// private InvCatalogConvertIF converter = null;
// this is how catalogRefs read their catalogs
InvCatalogFactory getCatalogFactory() {
return factory;
}
void setCatalogFactory(InvCatalogFactory factory) {
this.factory = factory;
}
// track converter
InvCatalogConvertIF getCatalogConverter() {
return converter;
}
void setCatalogConverter(InvCatalogConvertIF converter) {
this.converter = converter;
} */
/*
* Set the connverter to 1.0, typically to write a 0.6 out to a 1.0
*
public void setCatalogConverterToVersion1() {
setCatalogConverter(factory.getCatalogConverter(XMLEntityResolver.CATALOG_NAMESPACE_10));
} */
/**
* Write the catalog as an XML document to the specified stream.
*
* @param os write to this OutputStream
* @throws java.io.IOException on an error.
*/
public void writeXML(java.io.OutputStream os) throws java.io.IOException {
InvCatalogConvertIF converter = InvCatalogFactory.getDefaultConverter();
converter.writeXML(this, os);
}
/**
* Write the catalog as an XML document to the specified stream.
*
* @param os write to this OutputStream
* @param raw if true, write original (server) version, else write client version
* @throws java.io.IOException on an error.
*/
public void writeXML(java.io.OutputStream os, boolean raw) throws java.io.IOException {
InvCatalogConvertIF converter = InvCatalogFactory.getDefaultConverter();
converter.writeXML(this, os, raw);
}
//////////////////////////////////////////////////////////////////////////
/**
* Get dataset roots.
*
* @return List of InvProperty. May be empty, may not be null.
*/
public java.util.List getDatasetRoots() {
return roots;
}
/**
* Add Dataset Root, key = path, value = location.
* @param root add a dataset root
*/
public void addDatasetRoot(DataRootConfig root) {
roots.add(root);
}
/**
* InvCatalogImpl elements with same values are equal.
*/
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof InvCatalogImpl)) return false;
return o.hashCode() == this.hashCode();
}
/**
* Override Object.hashCode() to implement equals.
*/
public int hashCode() {
if (hashCode == 0) {
int result = 17;
if (null != getName())
result = 37 * result + getName().hashCode();
result = 37 * result + getServices().hashCode();
result = 37 * result + getDatasets().hashCode();
hashCode = result;
}
return hashCode;
}
private volatile int hashCode = 0; // Bloch, item 8
public boolean isStatic() {
return isStatic;
}
public void setStatic(boolean aStatic) {
isStatic = aStatic;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy