
thredds.catalog.InvCatalogImpl Maven / Gradle / Ivy
/*
* 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 - 2025 Weber Informatics LLC | Privacy Policy