All Downloads are FREE. Search and download functionalities are using the official Maven repository.

thredds.catalog.InvCatalogImpl Maven / Gradle / Ivy

Go to download

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