/////////////////////////////////////////////////////////////////////////////
// This file is part of the "Java-DAP" project, a Java implementation
// of the OPeNDAP Data Access Protocol.
//
// Copyright (c) 2010, OPeNDAP, Inc.
// Copyright (c) 2002,2003 OPeNDAP, Inc.
//
// Author: James Gallagher <jgallagher@opendap.org>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms,
// with or without modification, are permitted provided
// that the following conditions are met:
//
// - Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the distribution.
//
// - Neither the name of the OPeNDAP nor the names of its contributors may
//   be used to endorse or promote products derived from this software
//   without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/////////////////////////////////////////////////////////////////////////////

package opendap.dap;

import java.io.*;

/**
 * A vector of doubles.
 *
 * @author jehamby
 * @version $Revision: 15901 $
 * @see PrimitiveVector
 */
public class Float64PrimitiveVector extends PrimitiveVector
{

    /**
     * the array of double values.
     */
    private double vals[];

    /**
     * Constructs a new Float64PrimitiveVector.
     *
     * @param var the template BaseType to use.
     */
    public Float64PrimitiveVector(BaseType var) {
        super(var);
    }

    /**
     * Returns the number of elements in the array.
     *
     * @return the number of elements in the array.
     */
    public int getLength() {
        return vals.length;
    }

    /**
     * Sets the number of elements in the array.  Allocates a new primitive
     * array of the desired size.  Note that if this is called multiple times,
     * the old array and its contents will be lost.
     *

     * Only called inside of deserialize method or in derived
     * classes on server.
     *
     * @param len the number of elements in the array.
     */
    public void setLength(int len) {
        vals = new double[len];
    }

    /**
     * Return the i'th value as a double.
     *
     * @param i the index of the value to return.
     * @return the i'th value.
     */
    public final double getValue(int i) {
        return vals[i];
    }

    /**
     * Set the i'th value of the array.
     *
     * @param i      the index of the value to set.
     * @param newVal the new value.
     */
    public final void setValue(int i, double newVal) {
        vals[i] = newVal;
    }

    /**
     * Prints the value of all variables in this vector.  This
     * method is primarily intended for debugging OPeNDAP applications and
     * text-based clients such as geturl.
     *
     * @param os    the PrintWriter on which to print the value.
     * @param space this value is passed to the printDecl method,
     *              and controls the leading spaces of the output.
     * @see BaseType#printVal(PrintWriter, String, boolean)
     */
    public void printVal(PrintWriter os, String space) {
        int len = vals.length;
        for (int i = 0; i < len - 1; i++) {
            os.print(vals[i]);
            os.print(", ");
        }
        // print last value, if any, without trailing comma
        if (len > 0)
            os.print(vals[len - 1]);
    }

    /**
     * Prints the value of a single variable in this vector.
     * method is used by DArray's printVal method.
     *
     * @param os    the PrintWriter on which to print the value.
     * @param index the index of the variable to print.
     * @see DArray#printVal(PrintWriter, String, boolean)
     */
    public void printSingleVal(PrintWriter os, int index) {
        os.print(vals[index]);
    }

    /**
     * Reads data from a DataInputStream. This method is only used
     * on the client side of the OPeNDAP client/server connection.
     *
     * @param source   a DataInputStream to read from.
     * @param sv       The ServerVersion returned by the server.
     *                 (used by DSequence to determine which protocol version was
     *                 used).
     * @param statusUI The StatusUI object to use for GUI updates
     *                 and user cancellation notification (may be null).
     * @throws DataReadException when invalid data is read, or if the user
     *                           cancels the download.
     * @throws EOFException      if EOF is found before the variable is completely
     *                           deserialized.
     * @throws IOException       thrown on any other InputStream exception.
     * @see ClientIO#deserialize(DataInputStream, ServerVersion, StatusUI)
     */
    public synchronized void deserialize(DataInputStream source,
                                         ServerVersion sv,
                                         StatusUI statusUI)
            throws IOException, EOFException, DataReadException {
        for (int i = 0; i < vals.length; i++) {
            vals[i] = source.readDouble();
            if (statusUI != null) {
                statusUI.incrementByteCount(8);
                if (statusUI.userCancelled())
                    throw new DataReadException("User cancelled");
            }
        }
    }

    /**
     * Writes data to a DataOutputStream. This method is used
     * primarily by GUI clients which need to download OPeNDAP data, manipulate
     * it, and then re-save it as a binary file.
     *
     * @param sink a DataOutputStream to write to.
     * @throws IOException thrown on any OutputStream
     *                     exception.
     */
    public void externalize(DataOutputStream sink) throws IOException {
        for (int i = 0; i < vals.length; i++) {
            sink.writeDouble(vals[i]);
        }
    }

    /**
     * Write a subset of the data to a DataOutputStream.
     *
     * @param sink    a DataOutputStream to write to.
     * @param start  starting index (i=start)
     * @param stop   ending index (i<=stop)
     * @param stride index stride (i+=stride)
     * @throws IOException thrown on any OutputStream exception.
     */
    public void externalize(DataOutputStream sink, int start, int stop, int stride) throws IOException {
        for (int i = start; i <= stop; i += stride)
            sink.writeDouble(vals[i]);
    }

    /**
     * Returns (a reference to) the internal storage for this PrimitiveVector
     * object.
     *
WARNING:

     * Because this method breaks encapsulation rules the user must beware!
     * If we (the OPeNDAP prgramming team) choose to change the internal
     * representation(s) of these types your code will probably break.
     *

     * This method is provided as an optimization to eliminate massive
     * copying of data.
     *
     * @return The internal array of doubles.
     */
    public Object getInternalStorage() {
        return (vals);
    }

    /**
     * Set the internal storage for PrimitiveVector.
     *
WARNING:

     * Because this method breaks encapsulation rules the user must beware!
     * If we (the OPeNDAP prgramming team) choose to change the internal
     * representation(s) of these types your code will probably break.
     *

     * This method is provided as an optimization to eliminate massive
     * copying of data.
     */
    public void setInternalStorage(Object o) {
        vals = (double []) o;
    }

    /**
     * Create a new primitive vector using a subset of the data.
     *
     * @param start  starting index (i=start)
     * @param stop   ending index (i<=stop)
     * @param stride index stride (i+=stride)
     * @return new primitive vector, of type Float64PrimitiveVector.
     */
    public PrimitiveVector subset(int start, int stop, int stride) {
        Float64PrimitiveVector n = new Float64PrimitiveVector(getTemplate());
        stride = Math.max(stride, 1);
        stop = Math.max(start, stop);
        int length = 1 + (stop - start) / stride;
        n.setLength(length);

        int count = 0;
        for (int i = start; i <= stop; i += stride) {
            n.setValue(count, vals[i]);
            count++;
        }
        return n;
    }

/**
   * Prints the value of the variable, with its declaration.  This
   * function is primarily intended for debugging OPeNDAP applications and
   * text-based clients such as geturl.
   *
   * @param os           the PrintWriter on which to print the value.
   * @param space        this value is passed to the printDecl method,
   *                     and controls the leading spaces of the output.
   * @param print_decl_p a boolean value controlling whether the
   *                     variable declaration is printed as well as the value.
   */
   public void printVal(PrintWriter os, String space,
                                boolean print_decl_p) {}

    /**
     * Returns a clone of this Float64PrimitiveVector.
     * See DAPNode.cloneDag()
     *
     * @param map track previously cloned nodes
     * @return a clone of this Float32PrimitiveVector.
     */
    public DAPNode cloneDAG(CloneMap map)
        throws CloneNotSupportedException
    {
        Float64PrimitiveVector v = (Float64PrimitiveVector) super.cloneDAG(map);
        if (vals != null) {
            v.vals = new double[vals.length];
            System.arraycopy(vals, 0, v.vals, 0, vals.length);
        }
        return v;
    }

}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.nc2.ui.widget;

import ucar.nc2.ui.geoloc.CursorMoveEvent;
import ucar.nc2.ui.geoloc.CursorMoveEventListener;
import ucar.nc2.ui.geoloc.PickEvent;
import ucar.nc2.ui.geoloc.PickEventListener;
import ucar.nc2.util.ListenerManager;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;

/* A simple version of NavigatedPanel.
 * Allows drawing on it in scaled (world) coordinates.
 * It doesnt do its own drawing, but allows the caller to obtain its
 * BufferedImage and draw in it itself.
 *
 * @author John Caron
 */

public class ScaledPanel extends JPanel {
  private Color backColor = Color.white;
  private Rectangle2D screenBounds = new Rectangle2D.Double();
  private ScaledPanel.Bounds worldBounds = new ScaledPanel.Bounds();
  private Point2D worldPt = new Point2D.Double();

  private AffineTransform transform = new AffineTransform();
  private BufferedImage bImage = null;
  private MyImageObserver imageObs = new MyImageObserver();

  private ListenerManager lmPick, lmMove;

  private boolean debugDraw = false, debugTransform = false, debugBounds = false;

  public ScaledPanel() {
    setDoubleBuffered(false);

    // catch window resize events
    addComponentListener(new ComponentAdapter() {
      public void componentResized(ComponentEvent e) {
        Rectangle nb = getBounds();
        boolean sameSize = (nb.width == screenBounds.getWidth()) && (nb.height == screenBounds.getHeight());
        if (debugBounds) System.out.println("TPanel setBounds old= " + screenBounds);
        screenBounds.setRect(nb);

        if ((bImage != null) && sameSize)
          return;

        if (debugBounds) System.out.println("  newBounds = " + nb);
        // create new buffer the size of the window
        if ((nb.width > 0) && (nb.height > 0)) {
          bImage = new BufferedImage(nb.width, nb.height, BufferedImage.TYPE_INT_RGB); // why RGB ?
        } else {
          bImage = null;
        }
        transform = null;
      }
    });

    // listen for mouse events: throw "pick" events
    addMouseListener(new MouseAdapter() {
      public void mousePressed(MouseEvent e) {
        if (lmPick.hasListeners() && (transform != null)) {
          try {
            transform.inverseTransform(e.getPoint(), worldPt);
            lmPick.sendEvent(new PickEvent(ScaledPanel.this, worldPt));
          } catch (NoninvertibleTransformException nte) {
            ;
          }
        }
      }
    });

    // listen for mouse movement: throw "Cursor Move" events
    addMouseMotionListener(new MouseMotionAdapter() {
      public void mouseMoved(MouseEvent e) {
        if (lmMove.hasListeners() && (transform != null)) {
          try {
            transform.inverseTransform(e.getPoint(), worldPt);
            lmMove.sendEvent(new CursorMoveEvent(ScaledPanel.this, worldPt));
          } catch (NoninvertibleTransformException nte) {
            ;
          }
        }
      }
    });

    // manage Event Listener's
    lmPick = new ListenerManager(
            "ucar.nc2.ui.geoloc.PickEventListener",
            "ucar.nc2.ui.geoloc.PickEvent",
            "actionPerformed");

    lmMove = new ListenerManager(
            "ucar.nc2.ui.geoloc.CursorMoveEventListener",
            "ucar.nc2.ui.geoloc.CursorMoveEvent",
            "actionPerformed");
  }

  /**
   * Register a CursorMoveEventListener.
   */
  public void addCursorMoveEventListener(CursorMoveEventListener l) {
    lmMove.addListener(l);
  }

  /**
   * Remove a CursorMoveEventListener.
   */
  public void removeCursorMoveEventListener(CursorMoveEventListener l) {
    lmMove.removeListener(l);
  }

  /**
   * Register a PickEventListener.
   */
  public void addPickEventListener(PickEventListener l) {
    lmPick.addListener(l);
  }

  /**
   * Remove a PickEventListener.
   */
  public void removePickEventListener(PickEventListener l) {
    lmPick.removeListener(l);
  }

  /* set the bounds of the world coordinates.
  * The point (world.getX(), world.getY()) is mapped to the lower left point of the screen.
  * The point (world.getX() + world.Width(), world.getY()+world.Height()) is mapped
  * to the upper right corner. Therefore if coords decrease as you go up, world.Height()
  * should be negetive.
  */

  public void setWorldBounds(ScaledPanel.Bounds world) {
    worldBounds.set(world);
    transform = null;
    if (debugBounds) System.out.println("  setWorldBounds = " + worldBounds);
  }

  public ScaledPanel.Bounds getWorldBounds() {
    return worldBounds;
  }

  /**
   * User must get this Graphics2D and draw into it when panel needs redrawing
   */
  public Graphics2D getBufferedImageGraphics() {
    if (bImage == null)
      return null;
    Graphics2D g2 = bImage.createGraphics();

    // set graphics attributes
    if (transform == null)
      transform = calcTransform(screenBounds, worldBounds);
    g2.setTransform(transform);
    g2.setStroke(new BasicStroke(0.0f));      // default stroke size is one pixel
    g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
    g2.setBackground(backColor);
    g2.setClip(worldBounds.getRect());

    return g2;
  }

  /**
   * System-triggered redraw.
   */
  public void paintComponent(Graphics g) {
    if (bImage != null)
      g.drawImage(bImage, 0, 0, backColor, imageObs);
  }

  public void drawNow() {
    Graphics g = getGraphics();
    if (null != g) {
      paintComponent(g);
      g.dispose();
    }
  }

  /**
   * map world coords to screen coords.
   *
   * @param world  world coordinate rectangle
   * @param screen screen coordinate rectangle
   * @return AffineTransform for converting world to screen.
   */
  private AffineTransform calcTransform(Rectangle2D screen, Bounds world) {
    // scale to limiting dimension
    double xs = screen.getWidth() / (world.getRight() - world.getLeft());
    double ys = screen.getHeight() / (world.getLower() - world.getUpper());

    AffineTransform cat = new AffineTransform();
    cat.setToScale(xs, ys);
    cat.translate(-world.getLeft(), -world.getUpper());

    if (debugTransform) {
      System.out.println("TPanel calcTransform = ");
      System.out.println("  screen = " + screen);
      System.out.println("  world = " + world);

      System.out.println("  transform = " + cat.getScaleX() + " " + cat.getShearX() + " " + cat.getTranslateX());
      System.out.println("              " + cat.getShearY() + " " + cat.getScaleY() + " " + cat.getTranslateY());

      Point2D src = new Point2D.Double(world.getLeft(), world.getUpper());
      Point2D dst = new Point2D.Double(0.0, 0.0);

      System.out.println("  upper left pt = " + src);
      System.out.println("  transform = " + cat.transform(src, dst));

      src = new Point2D.Double(world.getRight(), world.getLower());
      System.out.println("  lower right pt = " + src);
      System.out.println("  transform = " + cat.transform(src, dst));
    }
    return cat;
  }

  // necessary for g.drawImage()

  private static class MyImageObserver implements ImageObserver {
    @Override
    public boolean imageUpdate(Image image, int flags, int x, int y, int width, int height) {
      return true;
    }
  }

  /**
   * Why the heck did I create yet another Rectangle?
   * Because I needed some precise semantics, especially for the y screen coordinate.
   * "upper" is what you want to be placed on the upper part of the screen,
   * "lower" on the lower part.
   */
  public static class Bounds {
    private double left, right, upper, lower;

    public Bounds() {
      this(0.0, 0.0, 0.0, 0.0);
    }

    public Bounds(double left, double right, double upper, double lower) {
      this.left = left;
      this.right = right;
      this.upper = upper;
      this.lower = lower;
    }

    public double getUpper() {
      return upper;
    }

    public double getLower() {
      return lower;
    }

    public double getRight() {
      return right;
    }

    public double getLeft() {
      return left;
    }

    public void set(Bounds b) {
      this.upper = b.getUpper();
      this.lower = b.getLower();
      this.right = b.getRight();
      this.left = b.getLeft();
    }

    public Rectangle2D getRect() {
      return new Rectangle2D.Double(Math.min(left, right), Math.min(lower, upper),
              Math.abs(right - left), Math.abs(lower - upper));
      //return new Rectangle2D.Double(Math.min(left,right), Math.min(lower,upper),
      //  Math.abs(right-left), Math.abs(lower-upper));
    }

    public String toString() {
      return "left: " + left + " right: " + right + " upper: " + upper + " lower: " + lower;
    }

  }

  public static void main(String[] argv) {

    Rectangle2D w = new Rectangle2D.Double(1.0, 1.0, 10.0, 10.0);
    Rectangle2D s = new Rectangle2D.Double(0.0, 0.0, 200, 100);

    double xs = s.getWidth() / w.getWidth();
    double ys = s.getHeight() / w.getHeight();

    AffineTransform cat = new AffineTransform();
    cat.setToScale(xs, -ys);
    cat.translate(-w.getX(), -w.getY() - w.getHeight());

    Point2D src = new Point2D.Double(1.0, 1.0);
    Point2D dst = new Point2D.Double(0.0, 0.0);

    System.out.println("  screen = " + s);
    System.out.println("  world = " + w);

    System.out.println("  pt = " + src);
    System.out.println("  transform = " + cat.transform(src, dst));

    src = new Point2D.Double(11.0, 11.0);
    System.out.println("  pt = " + src);
    System.out.println("  transform = " + cat.transform(src, dst));
  }

}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.httpservices;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;

import javax.annotation.concurrent.Immutable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Set;

/**
 * Provide Auth related utilities
 */

@Immutable
abstract public class HTTPAuthUtil
{

    //////////////////////////////////////////////////
    // Constants

    //////////////////////////////////////////////////
    // AuthScope Utilities

    /**
     * Given a session url AuthScope and a Method url AuthScope,
     * Indicate it the are "compatible" as defined as follows.
     * The method AuthScope is <i>compatible</i> with the session AuthScope
     * if its host+port is the same as the session's host+port and its scheme is
     * compatible, where e.g. http is compatible with https.
     * The scope realm is ignored.
     *
     * @param ss Session AuthScope
     * @param ms Method AuthScope
     * @return
     */
    static boolean
    authscopeCompatible(AuthScope ss, AuthScope ms)
    {
        assert (ss.getScheme() != null && ms.getScheme() != null);
        if(!ss.getHost().equalsIgnoreCase(ms.getHost()))
            return false;
        if(ss.getPort() != ms.getPort())
            return false;
        String sss = ss.getScheme().toLowerCase();
        String mss = ms.getScheme().toLowerCase();
        if(!sss.equals(mss)) {
            // Do some special casing
            if(sss.endsWith("s")) sss = sss.substring(0, sss.length() - 1);
            if(mss.endsWith("s")) mss = mss.substring(0, mss.length() - 1);
            if(!sss.equals(mss))
                return false;
        }
        return true;
    }

    /**
     * Given a session url AuthScope and a Method url AuthScope,
     * return a new AuthScope that is the upgrade/merge of the other two.
     * Here, upgrade changes the scheme (only) to move http -> https.
     * Assumes authscopeCompatible() is true.
     *
     * @param ss Session authScope
     * @param ms Method authScope
     * @return upgraded AuthScope.
     */
    static AuthScope
    authscopeUpgrade(AuthScope ss, AuthScope ms)
    {
        assert (HTTPAuthUtil.authscopeCompatible(ss, ms));
        String sss = ss.getScheme().toLowerCase();
        String mss = ms.getScheme().toLowerCase();
        String upgrade = sss;
        if(sss.startsWith("http") && mss.startsWith("http")) {
            if(sss.equals("https") || mss.equals("https"))
                upgrade = "https";
        }
        AuthScope host = new AuthScope(ss.getHost(), ss.getPort(), AuthScope.ANY_REALM, upgrade);
        return host;
    }

    static AuthScope
    uriToAuthScope(String surl)
            throws HTTPException
    {
        try {
            URI uri = HTTPUtil.parseToURI(surl);
            return uriToAuthScope(uri);
        } catch (URISyntaxException e) {
            throw new HTTPException(e);
        }
    }

    /**
     * Create an AuthScope from a URI; remove any principal
     *
     * @param uri to convert
     * @returns an AuthScope instance
     */

    static AuthScope
    uriToAuthScope(URI uri)
    {
        assert (uri != null);
        return new AuthScope(uri.getHost(), uri.getPort(), AuthScope.ANY_REALM, uri.getScheme());
    }

    static URI
    authscopeToURI(AuthScope authScope)
            throws HTTPException
    {
        try {
            URI url = new URI(authScope.getScheme(),
                    null,
                    authScope.getHost(),
                    authScope.getPort(),
                    "", null, null);
            return url;
        } catch (URISyntaxException mue) {
            throw new HTTPException(mue);
        }
    }

    static HttpHost authscopeToHost(AuthScope scope)
    {
        return new HttpHost(scope.getHost(), scope.getPort(), scope.getScheme());
    }

    static AuthScope hostToAuthScope(HttpHost host)
    {
        return new AuthScope(host.getHostName(), host.getPort(), AuthScope.ANY_REALM, host.getSchemeName());
    }

    static public AuthScope bestmatch(AuthScope scope, Set<AuthScope> scopelist)
    {
        Credentials creds = null;
        int bestMatchFactor = -1;
        AuthScope bestMatch = null;
        for(final AuthScope current : scopelist) {
            final int factor = scope.match(current);
            if(factor > bestMatchFactor) {
                bestMatchFactor = factor;
                bestMatch = current;
            }
        }
        return bestMatch;
    }

}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.nc2.iosp.grid;

import java.util.*;

/**
 * An "in memory" index for 2D grid files.
 * Has list of GridRecord (Parameter), GridDefRecord (GDS) and attributes.
 */
public final class GridIndex {

  /**
   * used to check versions of already created indexes.
   */
  static public final String current_index_version = "8.0";

  public final String filename;

  /**
   * Contains GridRecords
   */
  private final List<GridRecord> index = new ArrayList<GridRecord>();

  /**
   * Contains list of grid definitions (mostly projection info)
   */
  private final List<GridDefRecord> gcs = new ArrayList<GridDefRecord>();

  /**
   * contains global attributes of the Index.
   */
  private final Map<String, String> atts = new HashMap<String, String>();

  /**
   * Constructor for creating an Index from the Grid file.
   * Use the addXXX() methods.
   * @param filename name of data file, for debugging
   */
  public GridIndex(String filename) {
    this.filename = filename;
  }

  /**
   * GlobalAttributes of index.
   *
   * @return HashMap of type GlobalAttributes.
   */
  public final Map<String, String> getGlobalAttributes() {
    return atts;
  }

  /**
   * Grib records of index, one for each parameter.
   *
   * @return list of type GridRecord.
   */
  public final List<GridRecord> getGridRecords() {
    return index;
  }

  /**
   * GridDefs of the index.
   *
   * @return list of type GridDef.
   */
  public final List<GridDefRecord> getHorizCoordSys() {
    return gcs;
  }

  /**
   * adds a GridRecord to the index.
   *
   * @param gr GridRecord
   */
  public final void addGridRecord(GridRecord gr) {
    index.add(gr);
  }

  /**
   * adds a GridDefRecord to the index.
   *
   * @param gds GdsRecord
   */
  public final void addHorizCoordSys(GridDefRecord gds) {
    gcs.add(gds);
  }

  /**
   * adds a GlobalAttribute to the index.
   *
   * @param name  GlobalAttribute
   * @param value String
   */
  public final void addGlobalAttribute(String name, String value) {
    atts.put(name, value);
  }

  /**
   * Get the count of GridRecords
   *
   * @return the count of GridRecords
   */
  public int getGridCount() {
    return index.size();
  }

  /**
   * compares GDS for duplicates
   */
  public void finish() {
    if (gcs.size() == 1)
      return;
    if (gcs.size() == 2) {
      List hcs = getHorizCoordSys();
      GridDefRecord.compare((GridDefRecord) hcs.get(0), (GridDefRecord) hcs.get(1));
    }
  }

}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.units;

import javax.annotation.concurrent.Immutable;
import java.io.Serializable;

/**
 * Provides support for a Base/exponent pair. Instances of this class are
 * immutable.
 *
 * @author Steven R. Emmerson
 */
@Immutable
public final class Factor implements Serializable {
	private static final long	serialVersionUID	= 1L;

	/**
	 * The Base entity.
	 *
	 * @serial
	 */
	private final Base			_base;

	/**
	 * The exponent.
	 *
	 * @serial
	 */
	private final int			_exponent;

	/**
	 * Constructs from a Base. The exponent is set to unity.
	 *
	 * @param base
	 *            The base entity.
	 */
	public Factor(final Base base) {
		this(base, 1);
	}

	/**
	 * Constructs from a Factor and an exponent.
	 *
	 * @param factor
	 *            The factor.
	 * @param exponent
	 *            The exponent.
	 */
	public Factor(final Factor factor, final int exponent) {
		this(factor.getBase(), exponent);
	}

	/**
	 * Constructs from a Base and an exponent.
	 *
	 * @param base
	 *            The base entity.
	 * @param exponent
	 *            The exponent.
	 */
	public Factor(final Base base, final int exponent) {
		_base = base;
		_exponent = exponent;
	}

	/**
	 * Returns the Base entity.
	 *
	 * @return The Base entity.
	 */
	public Base getBase() {
		return _base;
	}

	/**
	 * Returns the identifier of the Base entity.
	 *
	 * @return The identifier of the Base entity (symbol or name).
	 */
	public String getID() {
		return getBase().getID();
	}

	/**
	 * Returns the exponent of the Base entity.
	 *
	 * @return The exponent of the Base entity.
	 */
	public int getExponent() {
		return _exponent;
	}

	/**
	 * Raises this Factor to a power.
	 *
	 * @param power
	 *            The power by which to raise this Factor.
	 * @return The Factor resulting from raising this Factor to the given power.
	 */
	public Factor pow(final int power) {
		return new Factor(getBase(), getExponent() * power);
	}

	/**
	 * Returns the string representation of this Factor.
	 *
	 * @return The string representation of this Factor.
	 */
	@Override
	public final String toString() {
		return getExponent() == 0
				? ""
				: getExponent() == 1
						? getBase().toString()
						: getBase().toString() + getExponent();
	}

	/**
	 * Indicates if this Factor is semantically identical to another object.
	 *
	 * @param object
	 *            The object.
	 * @return true if and only if this Factor is semantically
	 *         identical to object<
     *				/code>.
	 */
	@Override
	public boolean equals(final Object object) {
		boolean equals;
		if (this == object) {
			equals = true;
		}
		else if (!(object instanceof Factor)) {
			equals = false;
		}
		else {
			final Factor that = (Factor) object;
			equals = getExponent() != that.getExponent()
					? false
					: getExponent() == 0 || getBase().equals(that.getBase());
		}
		return equals;
	}

	/**
	 * Returns the hash code of this instance.
	 *
	 * @return The hash code of this instance.
	 */
	@Override
	public int hashCode() {
		return getExponent() == 0
				? getClass().hashCode()
				: getExponent() ^ getBase().hashCode();
	}

	/**
	 * Indicates if this Factor is the reciprocal of another Factor.
	 *
	 * @param that
	 *            The other factor.
	 * @return true if and only if this Factor is the reciprocal of
	 *         that.
	 */
	public boolean isReciprocalOf(final Factor that) {
		return getBase().equals(that.getBase())
				&& getExponent() == -that.getExponent();
	}

	/**
	 * Indicates if this factor is dimensionless. A Factor is dimensionless if
	 * and only if the exponent is zero or the Base entity is dimensionless.
	 *
	 * @return true if and only if this Factor is dimensionless.
	 */
	public boolean isDimensionless() {
		return getExponent() == 0 || getBase().isDimensionless();
	}
}


package ucar.nc2.ogc.waterml;

import net.opengis.swe.x20.UnitReference;
import net.opengis.waterml.x20.TVPMeasurementMetadataType;
import ucar.nc2.VariableSimpleIF;
import ucar.nc2.ogc.gml.NcReferenceType;
import ucar.nc2.ogc.swe.NcUnitReference;

/**
 * Created by cwardgar on 2014/03/06.
 */
public abstract class NcTVPMeasurementMetadataType {
    // wml2:Collection/wml2:observationMember/om:OM_Observation/om:result/wml2:MeasurementTimeseries/
    //         wml2:defaultPointMetadata/wml2:DefaultTVPMeasurementMetadata
    public static TVPMeasurementMetadataType initDefaultTVPMeasurementMetadata(
            TVPMeasurementMetadataType defaultTVPMeasurementMetadata, VariableSimpleIF dataVar) {
        // wml2:uom
        UnitReference uom = NcUnitReference.initUom(defaultTVPMeasurementMetadata.addNewUom(), dataVar);
        if (uom == null) {
            defaultTVPMeasurementMetadata.unsetUom();
        }

        // wml2:interpolationType
        NcReferenceType.initInterpolationType(defaultTVPMeasurementMetadata.addNewInterpolationType());

        return defaultTVPMeasurementMetadata;
    }

    private NcTVPMeasurementMetadataType() { }
}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package thredds.client.catalog;

import ucar.nc2.util.Indent;

import javax.annotation.concurrent.Immutable;
import java.net.URI;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

/**
 *  Client catalog service
 *
 * @author caron
 * @since 1/7/2015
 */
@Immutable
public class Service {            // (7)
  private final String name;
  private final String base;
  private final String typeS;
  private final String desc;
  private final String accessType;
  private final String suffix;
  private final List<Service> nestedServices;
  private final List<Property> properties;

  public Service(String name, String base, String typeS, String desc, String suffix, List<Service> nestedServices, List<Property> properties, String accessType) {
    this.name = name;
    this.base = base; // (base.length() == 0 || base.endsWith("/")) ? base : base + "/";
    this.typeS = typeS;
    this.desc = desc;
    this.accessType = accessType;
    this.suffix = suffix;
    this.nestedServices = nestedServices;
    this.properties = properties;
  }

  public String getName() {
    return name;
  }

  public String getBase() {
    return base;
  }

  public String getServiceTypeName() {
    return typeS;
  }
  public ServiceType getType() {
    return ServiceType.getServiceTypeIgnoreCase(typeS);
  }

  public String getDesc() {
    return desc;
  }

  public String getAccessType() { return accessType; }

  public String getSuffix() {
    return suffix == null ? "" : suffix;
  }

  public List<Service> getNestedServices() {
    return nestedServices == null ? new ArrayList<>(0) : nestedServices;
  }

  public List<Property> getProperties() {
    return properties == null ? new ArrayList<Property>(0) : properties;
  }

  /**
   * See if the service Base is reletive
   * @return true if the service Base is reletive
   */
  public boolean isRelativeBase() {
    if (getType() == ServiceType.Compound)
      return true;

    try {
      URI uri = new java.net.URI(base);
      return !uri.isAbsolute();
    } catch (java.net.URISyntaxException e) {
      throw new IllegalArgumentException(e.getMessage());
    }
  }

  protected String toString(Indent indent) {
    Formatter f = new Formatter();
    f.format("%sService{ name=%s, base=%s, typeS=%s", indent, name, base, typeS);
    if (desc != null) f.format(",desc=%s", desc);
    if (suffix != null) f.format(",suffix=%s", suffix);

    if (properties != null) {
      f.format("%n");
      indent.incr();
      for (Property p : properties)
        f.format("%s%s%n", indent, p.toString());
      indent.decr();
      f.format("%n");
    }

    if (nestedServices != null) {
      f.format("%n");
      indent.incr();
      for (Service nested : nestedServices)
        f.format("%s%n", nested.toString(indent));
      indent.decr();
      f.format("}%n");
    } else
      f.format("}");

    return f.toString();
  }

  @Override
  public String toString() {
    return toString(new Indent(2));
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Service service = (Service) o;

    if (base != null ? !base.equals(service.base) : service.base != null) return false;
    if (desc != null ? !desc.equals(service.desc) : service.desc != null) return false;
    if (name != null ? !name.equals(service.name) : service.name != null) return false;
    if (nestedServices != null ? !nestedServices.equals(service.nestedServices) : service.nestedServices != null) return false;
    if (properties != null ? !properties.equals(service.properties) : service.properties != null) return false;
    if (suffix != null ? !suffix.equals(service.suffix) : service.suffix != null) return false;
    if (typeS != null ? !typeS.equals(service.typeS) : service.typeS != null) return false;

    return true;
  }

  @Override
  public int hashCode() {
    int result = name != null ? name.hashCode() : 0;
    result = 31 * result + (base != null ? base.hashCode() : 0);
    result = 31 * result + (typeS != null ? typeS.hashCode() : 0);
    result = 31 * result + (desc != null ? desc.hashCode() : 0);
    result = 31 * result + (suffix != null ? suffix.hashCode() : 0);
    result = 31 * result + (nestedServices != null ? nestedServices.hashCode() : 0);
    result = 31 * result + (properties != null ? properties.hashCode() : 0);
    return result;
  }
}


/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package thredds.servlet;

import org.slf4j.MDC;

import javax.servlet.http.HttpServletRequest;
import java.util.Formatter;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Utility class for gathering context information for use in log messages.
 * Includes methods appropriate when the context thread is an individual
 * HTTP request and when the context thread is an initialization thread.
 * The context information is contained in a key/value map.
 *

 *
Uses the SLF4J MDC framework (see @link{org.slf4j.MDC} for more details).
 *

 *
If properly configured, each log entry within the context thread
 * will include the gathered context information. For instance, in log4j
 * and slf4j, the appender pattern would contain strings with the form
 * "%X{<contextKey>}", where "<contextKey>" is a context key
 * value. The context key strings are given in each setup method below.
 *
 * @author caron
 * @see org.slf4j.MDC
 * @since Jan 9, 2009
 */
public class UsageLog {
  private static final AtomicLong logServerAccessId = new AtomicLong(0);

  /**
   * Gather context information for the given HTTP request and return
   * a log message appropriate for logging at the start of the request.
   *

   *
The following context information is gathered:
   *

   *
"ID" - an identifier for the current thread;

   *
"host" - the remote host (IP address or host name);

   *
"userid" - the id of the remote user;

   *
"startTime" - the system time in millis when this request is started (i.e., when this method is called); and

   *
"request" - The HTTP request, e.g., "GET /index.html HTTP/1.1".

   *

   *

   *
Call this method at the start of each HttpServlet doXXX() method
   * (e.g., doGet(), doPut()) or Spring MVC Controller handle() method.
   *
   * @param req the current request
   * @return a log message appropriate for the start of the request.
   */
  public static String setupRequestContext(HttpServletRequest req) {

    // Setup context.
    //HttpSession session = req.getSession(false);
    /* MDC.put("host", req.getRemoteHost());
    MDC.put("ident", (session == null) ? "-" : session.getId());
    MDC.put("userid", req.getRemoteUser() != null ? req.getRemoteUser() : "-"); */

    MDC.put("ID", Long.toString(logServerAccessId.incrementAndGet()));
    MDC.put("startTime", Long.toString(System.currentTimeMillis()));

    String query = req.getQueryString();
    query = (query != null) ? "?" + query : "";
    Formatter request = new Formatter();
    request.format("\"%s %s%s %s\"", req.getMethod(), req.getRequestURI(), query, req.getProtocol());

    MDC.put("request", request.toString());
    return "Remote host: " + req.getRemoteHost() + " - Request: " + request.toString();
  }

  /**
   * Return a log message appropriate for logging at the completion of
   * the contexts HTTP request.
   *

   *
Call this method at every exit point in each HttpServlet doXXX() method
   * (e.g., doGet(), doPut()) or Spring MVC Controller handle() method.
   *
   * @param resCode        - the result code for this request.
   * @param resSizeInBytes - the number of bytes returned in this result, -1 if unknown.
   * @return closing log message
   */
  public static String closingMessageForRequestContext(int resCode, long resSizeInBytes) {
    long duration = calculateElapsedTime();

    return "Request Completed - " + resCode + " - " + resSizeInBytes + " - " + duration;
  }

  /**
   * Gather context information for the current non-request thread and
   * return a log message appropriate for logging.
   *

   *
The following context information is gathered:
   *

   *
"ID" - an identifier for the current thread; and

   *
"startTime" - the system time in millis when this method is called.

   *

   *

   *
Call this method only for non-request servlet activities, e.g.,
   * during init() or destroy().
   * @return starting log message
   */
  public static String setupNonRequestContext() {
    // Setup context.
    MDC.put("ID", Long.toString(logServerAccessId.incrementAndGet()));
    MDC.put("startTime", Long.toString(System.currentTimeMillis()));

    return "Non-request thread opening.";
  }

  /**
   * Return a log message appropriate for logging at the close of
   * the non-request context.
   *
   * @return a log message appropriate for logging at the close of the non-request context.
   */
  public static String closingMessageNonRequestContext() {
    long duration = calculateElapsedTime();

    return "Non-request thread closing - " + duration;
  }

  private static long calculateElapsedTime() {
    long endTime = System.currentTimeMillis();
    String startTimeS = MDC.get("startTime");
    if (startTimeS == null) return -1;
    long startTime = Long.parseLong(startTimeS);
    return endTime - startTime;
  }

  public void add2map(String key, String value) {
    MDC.put(key, value);
  }
}


/* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
   See the LICENSE file for more information. */

package dap4.servlet;

import dap4.core.util.IndentWriter;
import dap4.dap4lib.DapProtocol;
import dap4.dap4lib.RequestMode;

import java.io.IOException;
import java.io.StringWriter;

/**
 * Generate the DSR for a dataset.
 * Currently only generates a minimal DSR.
 */

public class DapDSR
{

    //////////////////////////////////////////////////
    // Constants

    static final boolean DEBUG = false;

    //////////////////////////////////////////////////
    // Constructor(s)

    public DapDSR()
    {
    }

    //////////////////////////////////////////////////
    // Accessors

    //////////////////////////////////////////////////
    // API

    public String generate(String dataseturl)
        throws IOException
    {
        StringWriter sw = new StringWriter();
        IndentWriter printer = new IndentWriter(sw);
        printer.marginPrintln("<DatasetServices");
        printer.indent(2);
        printer.marginPrintln("xmlns=\"http://xml.opendap.org/ns/DAP/4.0/dataset-services#\">");
        printer.outdent();
        printer.marginPrint("<DapVersion>");
        printer.print(DapProtocol.X_DAP_VERSION);
        printer.println("</DapVersion>");
        printer.marginPrint("<ServerSoftwareVersion>");
        printer.print(DapProtocol.X_DAP_SERVER);
        printer.println("</ServerSoftwareVersion>");

        printer.marginPrintln("<Service title=\"DAP4 Dataset Services\"");
        printer.indent(3);
        printer.marginPrintln("role=\"http://services.opendap.org/dap4/dataset-services\">");
        printer.outdent(3);
        printer.indent();
        printer.marginPrint("<link type=\"");
        printer.print(DapProtocol.contenttypes.get(RequestMode.DSR).contenttype);
        printer.println("\"");
        printer.indent(2);
        printer.marginPrint("href=\"");
        printer.print(dataseturl);
        printer.println("\">");
        printer.outdent(2);
        printer.indent();
        printer.marginPrintln("<alt type=\"text/xml\"/>");
        printer.outdent();
        printer.marginPrintln("</link>");
        printer.marginPrintln("<link type=\"text/xml\"");
        printer.indent(2);
        printer.marginPrint("href=\"");
        printer.print(dataseturl);
        printer.println(".xml\"/>");
        printer.outdent(2);
        printer.outdent();
        printer.marginPrintln("</Service>");

        printer.marginPrintln("<Service title=\"DAP4 Dataset Metadata\"");
        printer.indent(3);
        printer.marginPrintln("role=\"http://services.opendap.org/dap4/dataset-metadata\">");
        printer.outdent(3);
        printer.indent();
        printer.marginPrint("<link type=\"");
        printer.print(DapProtocol.contenttypes.get(RequestMode.DMR).contenttype);
        printer.println("\"");
        printer.indent(2);
        printer.marginPrint("href=\"");
        printer.print(dataseturl);
        printer.println(".dmr\">");
        printer.outdent(2);
        printer.indent();
        printer.marginPrintln("<alt type=\"text/xml\"/>");
        printer.outdent();
        printer.marginPrintln("</link>");
        printer.marginPrintln("<link type=\"text/xml\"");
        printer.indent(2);
        printer.marginPrint("href=\"");
        printer.print(dataseturl);
        printer.println(".dmr.xml\"/>");
        printer.outdent(2);
        printer.outdent();
        printer.marginPrintln("</Service>");

        printer.marginPrintln("<Service title=\"DAP4 Dataset Data\"");
        printer.indent(2);
        printer.marginPrintln("role=\"http://services.opendap.org/dap4/data\">");
        printer.outdent(2);
        printer.indent();
        printer.marginPrint("<link type=\"");
        printer.print(DapProtocol.contenttypes.get(RequestMode.DAP).contenttype);
        printer.println("\"");
        printer.indent(2);
        printer.marginPrint("href=\"");
        printer.print(dataseturl);
        printer.println(".dap\"/>");
        printer.outdent(2);
        printer.outdent();
        printer.marginPrintln("</Service>");
        printer.outdent();
        printer.marginPrintln("</DatasetServices>");

        printer.flush();
        printer.close();
        sw.close();
        return sw.toString();
    }

} // DapDSR


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
// $Id: DirectoryScanner.java 63 2006-07-12 21:50:51Z edavis $
package thredds.cataloggen;

import thredds.catalog.InvCatalog;
import thredds.catalog.InvDataset;
import thredds.catalog.InvDatasetImpl;
import thredds.catalog.InvService;
import thredds.cataloggen.catalogrefexpander.BooleanCatalogRefExpander;
import thredds.cataloggen.datasetenhancer.RegExpAndDurationTimeCoverageEnhancer;
import thredds.crawlabledataset.CrawlableDataset;
import thredds.crawlabledataset.CrawlableDatasetFactory;
import thredds.crawlabledataset.CrawlableDatasetFilter;
import thredds.crawlabledataset.filter.MultiSelectorFilter;
import thredds.crawlabledataset.filter.RegExpMatchOnNameFilter;
import thredds.crawlabledataset.sorter.LexigraphicByNameSorter;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

/**
 * Allow generation of THREDDS InvCatalog documents from the contents of a local directory.
 *
 * @author edavis
 * @since 2004-12-10T15:33:32-0700
 */
public class DirectoryScanner
{
  static private org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DirectoryScanner.class);

  private String serviceTitle;
  private File serviceBaseUrlDir;
  private CrawlableDataset collectionCrDs;

  private String prefixPath;
  private boolean createCatalogRefs = true;

  private InvService service;

  /**
   * Construct a DirectoryScanner given information about the data server to be cataloged.
   *
   * @param service - the service for the datasets found in the directory scanned.
   * @param serviceTitle - a title for the service (used as the title of the top-level dataset).
   * @param serviceBaseUrlDir - the local directory to which the service's base URL references.
   * @param prefixPath - path name to append to the urlPath of resulting datasets, can be null.
   * @param createCatalogRefs - if true, generate a catalogRef for each directory, otherwise, recurse into directories.
   *
   * @throws IllegalArgumentException if invalid service type is given or root directory is not a directory.
   */
  public DirectoryScanner( InvService service, String serviceTitle, File serviceBaseUrlDir, String prefixPath, boolean createCatalogRefs )
  {
    this.service = service;
    this.serviceTitle = serviceTitle;
    this.serviceBaseUrlDir = serviceBaseUrlDir;
    try
    {
      collectionCrDs = CrawlableDatasetFactory.createCrawlableDataset( serviceBaseUrlDir.getAbsolutePath(), null, null );
    }
    catch ( IOException e )
    {
      // @todo Should throw an IOException!
      throw new IllegalArgumentException( "IOException while creating dataset: " + e.getMessage() );
    }
    catch ( ClassNotFoundException e )
    {
      throw new IllegalArgumentException( "Did not find class: " + e.getMessage() );
    }
    catch ( NoSuchMethodException e )
    {
      throw new IllegalArgumentException( "Required constructor not found in class: " + e.getMessage() );
    }
    catch ( IllegalAccessException e )
    {
      throw new IllegalArgumentException( "Did not have necessary access to class: " + e.getMessage() );
    }
    catch ( InvocationTargetException e )
    {
      throw new IllegalArgumentException( "Could not invoke required method in class: " + e.getMessage() );
    }
    catch ( InstantiationException e )
    {
      throw new IllegalArgumentException( "Could not instatiate class: " + e.getMessage() );
    }
    if ( ! collectionCrDs.isCollection())
      throw new IllegalArgumentException( "Base URL directory is not a directory <" + serviceBaseUrlDir.getAbsolutePath() + ">.");

    this.prefixPath = prefixPath;
    this.createCatalogRefs = createCatalogRefs;

//    log.debug( "DirectoryScanner(): service="+service.getName()+"; serviceTitle="+serviceTitle+"; serviceBaseUrlDir="+serviceBaseUrlDir+"; createCatalogRefs="+createCatalogRefs);
//    this.serviceTitle = serviceTitle;
//    this.serviceBaseUrlDir = serviceBaseUrlDir;
//    if ( this.serviceBaseUrlDir.getPath().indexOf( "*") == -1)
//    {
//      if ( ! this.serviceBaseUrlDir.isDirectory()) throw new IllegalArgumentException( "Base URL directory is not a directory <" + serviceBaseUrlDir.getAbsolutePath() + ">.");
//    }
//    this.service = new ResultService( service, serviceBaseUrlDir.getAbsolutePath());
//
//    this.prefixPath = prefixPath;
//    this.createCatalogRefs = createCatalogRefs;
  }

  /**
   * Return a catalog for the given directory.
   *
   * @param directory - the directory to represent as a catalog.
   * @param filterPattern
   * @param sortInIncreasingOrder
   * @return An InvCatalog that represents the given directory of datasets.
   *
   * @throws NullPointerException if the given directory is null.
   * @throws IllegalArgumentException if the given directory is not actually a directory or if it is not under the service base URL directory.
   */
  public InvCatalog getDirCatalog( File directory, String filterPattern, boolean sortInIncreasingOrder, boolean addDatasetSize )
  {
    return( this.getDirCatalog( directory, filterPattern, sortInIncreasingOrder, null, addDatasetSize, null, null, null) );
  }

  public InvCatalog getDirCatalog( File directory, String filterPattern, boolean sortInIncreasingOrder, String addIdBase, boolean addDatasetSize, String dsNameMatchPattern, String startTimeSubstitutionPattern, String duration )
  {
    CrawlableDataset catalogCrDs;
    try
    {
      catalogCrDs = CrawlableDatasetFactory.createCrawlableDataset( directory.getAbsolutePath(), null, null );
    }
    catch ( IOException e )
    {
      // @todo Should throw an IOException!
      throw new IllegalArgumentException( "IOException while creating dataset: " + e.getMessage() );
    }
    catch ( ClassNotFoundException e )
    {
      throw new IllegalArgumentException( "Did not find class: " + e.getMessage() );
    }
    catch ( NoSuchMethodException e )
    {
      throw new IllegalArgumentException( "Required constructor not found in class: " + e.getMessage() );
    }
    catch ( IllegalAccessException e )
    {
      throw new IllegalArgumentException( "Did not have necessary access to class: " + e.getMessage() );
    }
    catch ( InvocationTargetException e )
    {
      throw new IllegalArgumentException( "Could not invoke required method in class: " + e.getMessage() );
    }
    catch ( InstantiationException e )
    {
      throw new IllegalArgumentException( "Could not instatiate class: " + e.getMessage() );
    }
    if ( ! catalogCrDs.isCollection() )
      throw new IllegalArgumentException( "catalog directory is not a directory <" + serviceBaseUrlDir.getAbsolutePath() + ">." );

    return getDirCatalog( catalogCrDs, filterPattern, sortInIncreasingOrder, addIdBase, addDatasetSize, dsNameMatchPattern, startTimeSubstitutionPattern, duration );
  }
  public InvCatalog getDirCatalog( CrawlableDataset catalogCrDs, String filterPattern, boolean sortInIncreasingOrder, String addIdBase, boolean addDatasetSize, String dsNameMatchPattern, String startTimeSubstitutionPattern, String duration )
  {

    // Setup the filter
    CrawlableDatasetFilter filter = null;
    if ( filterPattern != null )
    {
      // Include atomic datasets that match the given filter string.
      MultiSelectorFilter.Selector selector = new MultiSelectorFilter.Selector( new RegExpMatchOnNameFilter( filterPattern), true, true, false );
      filter = new MultiSelectorFilter( selector );
    }
    else
    {
      filter = new RegExpMatchOnNameFilter( ".*" );
    }
    List enhancerList = null;
    if ( dsNameMatchPattern != null
         && startTimeSubstitutionPattern != null
         && duration != null )
    {
      enhancerList = new ArrayList();
      enhancerList.add( RegExpAndDurationTimeCoverageEnhancer.getInstanceToMatchOnDatasetName( dsNameMatchPattern, startTimeSubstitutionPattern, duration ) );
    }
    CatalogBuilder catBuilder = new StandardCatalogBuilder( prefixPath, null, collectionCrDs, filter, service,
                                                            addIdBase, null, null, addDatasetSize,
                                                            new LexigraphicByNameSorter( sortInIncreasingOrder ), null, enhancerList , null,
                                                            new BooleanCatalogRefExpander( ! this.createCatalogRefs) );

    InvCatalog catalog;
    try
    {
      catalog = catBuilder.generateCatalog( catalogCrDs );
    }
    catch ( IOException e )
    {
      throw new IllegalArgumentException( "Could not generate catalog: " + e.getMessage() );
    }

//    log.debug( "getDirCatalog(): directory=" + directory + "; filterPattern=" + filterPattern + "; sortInIncreasingOrder=" + sortInIncreasingOrder + "; addIdBase="+addIdBase+"; dsNameMatchPattern=" + dsNameMatchPattern + "; startTimeSubstitutionPattern=" + startTimeSubstitutionPattern + "; duration=" + duration );
//    if ( !directory.isDirectory() )
//    {
//      String tmpMsg = "Given directory is not a directory <" + directory.getAbsolutePath() + ">.";
//      log.warn( tmpMsg);
//      throw new IllegalArgumentException( tmpMsg );
//    }
//    DatasetSource dsSource = DatasetSource.newDatasetSource( directory.getName(),
//                                                             DatasetSourceType.LOCAL, DatasetSourceStructure.DIRECTORY_TREE,
//                                                             directory.getAbsolutePath(), this.service );
//    dsSource.setPrefixUrlPath( this.prefixPath );
//    dsSource.setCreateCatalogRefs( this.createCatalogRefs );
//    dsSource.setAddDatasetSize( addDatasetSize );
//    if ( filterPattern != null )
//    {
//      DatasetFilter datasetFilter = new DatasetFilter( dsSource, "Filter files on \"" + filterPattern + "\"",
//                                                       DatasetFilter.Type.REGULAR_EXPRESSION, filterPattern );
//      datasetFilter.setMatchPatternTarget( "name");
//      dsSource.addDatasetFilter( datasetFilter );
//      datasetFilter = new DatasetFilter( dsSource, "Allow all dirs",
//                                         DatasetFilter.Type.REGULAR_EXPRESSION,
//                                         "", true, false, false );
//      datasetFilter.setMatchPatternTarget( "name" );
//      dsSource.addDatasetFilter( datasetFilter );
//    }
//    dsSource.setDatasetSorter( new DatasetSorter( sortInIncreasingOrder));
//    if ( dsNameMatchPattern != null
//         && startTimeSubstitutionPattern != null
//         && duration != null)
//    {
//      dsSource.addDatasetEnhancer( DatasetEnhancer1.createAddTimeCoverageEnhancer( dsNameMatchPattern, startTimeSubstitutionPattern, duration));
//    }
//
//    if ( addIdBase != null)
//    {
//      dsSource.addDatasetEnhancer( DatasetEnhancer1.createAddIdEnhancer( addIdBase));
//    }
//
//    InvCatalog cat = null;
//    try
//    {
//      cat = dsSource.fullExpand();
//    }
//    catch ( IOException e )
//    {
//      throw new IllegalArgumentException( "Given directory is not a collection dataset <" + directory.getAbsolutePath() + ">: " + e.getMessage() );
//    }
    InvDataset topDs = catalog.getDataset();
    if ( collectionCrDs.getPath().equals( catalogCrDs.getPath() ) && this.serviceTitle != null )
    //if ( topDs.getName().equals( "" ) && this.serviceTitle != null )
    {
      logger.warn( "getDirCatalog(): top dataset name is null, setting to serviceTitle <" + this.serviceTitle + ">");
      ( (InvDatasetImpl) topDs ).setName( this.serviceTitle );
    }

    return ( catalog );
  }

//  public InvCatalog getDirCatalog( String dirPattern, String filterPattern, boolean sortInIncreasingOrder, boolean addDatasetSize )
//  {
//    return( this.getDirCatalog( dirPattern, filterPattern, sortInIncreasingOrder, null, addDatasetSize, null, null, null) );
//  }

//  public InvCatalog getDirCatalog( String dirPattern, String filterPattern, boolean sortInIncreasingOrder, String addIdBase, boolean addDatasetSize, String dsNameMatchPattern, String startTimeSubstitutionPattern, String duration )
//  {
//    // If no wildcards in directory pattern, return catalog for single directory.
//    if ( dirPattern.indexOf( "*") == -1) return( this.getDirCatalog( new File( dirPattern), filterPattern, sortInIncreasingOrder, addIdBase, addDatasetSize, dsNameMatchPattern, startTimeSubstitutionPattern, duration));
//
//    //
//    log.debug( "dirPattern=" + dirPattern + " - filterPattern=" + filterPattern);
//    int index1 = dirPattern.lastIndexOf( "/", dirPattern.indexOf( "*"));
//    int index2 = dirPattern.indexOf( "/", index1+1 );
//    log.debug( "[" + index1 + "] - [" + index2 + "]");
//    String pathName1 = dirPattern.substring( 0, index1);
//    StringBuffer pathNameWC = new StringBuffer( dirPattern.substring(  index1+1, index2) );
//    String pathName2 = dirPattern.substring(  index2+1);
//    log.debug( "dirPattern <" + dirPattern + ">=<" + pathName1 + "[" + index1 + "]" + pathNameWC + "[" + index2 + "]" + pathName2 + ">");
//
//    File startDir = new File( pathName1 );
//    if ( ! startDir.isDirectory())
//    {
//      String tmpMsg = "Not a directory <" + startDir.getPath() + ">";
//      log.warn( tmpMsg);
//      throw new IllegalArgumentException( tmpMsg);
//    }
//    File[] curMatchFiles = startDir.listFiles( new MyFileFilter( pathNameWC.toString()));
//    log.debug( "Match Files:" );
//    List fullMatchDirs = new ArrayList();
//    for( int i=0; i<curMatchFiles.length; i++)
//    {
//      log.debug( "  [" + i + "]=" + curMatchFiles[ i] );
//      String curMatchPathName = pathName2 == null ? curMatchFiles[i].getPath() : curMatchFiles[i].getPath() + "/" + pathName2;
//      if ( new File( curMatchPathName).isDirectory())
//      {
//        fullMatchDirs.add( curMatchPathName);
//      }
//    }
//
//    // Get catalogs for each matching path.
//    List matchPathCatalogs = new ArrayList();
//    for ( Iterator it = fullMatchDirs.iterator(); it.hasNext(); )
//    {
//      String curMatchPath = (String) it.next();
//      InvCatalog curCatalog = this.getDirCatalog( new File( curMatchPath), filterPattern, sortInIncreasingOrder, addIdBase, addDatasetSize, dsNameMatchPattern, startTimeSubstitutionPattern, duration );
//      matchPathCatalogs.add( curCatalog);
//    }
//
//    return( this.combineCatalogs( dirPattern, matchPathCatalogs));
//  }

//  private InvCatalog combineCatalogs( String combinedName, List catalogs)
//  {
//    if ( catalogs == null) throw new IllegalArgumentException( "Null catalog list.");
//    if ( catalogs.isEmpty()) throw new IllegalArgumentException( "Empty catalog list.");
//    InvCatalog cat = (InvCatalog) catalogs.get(0);
//    InvDataset top = (InvDataset) cat.getDataset();
//    for ( int i = 1; i < catalogs.size(); i++ )
//    {
//      InvCatalog curCat = (InvCatalog) catalogs.get( i );
//      top.getDatasets().addAll( curCat.getDataset().getDatasets() );
//    }
//
//    // @todo sort the datasets.
//
//    // Rename catalog with combined name.
//    if ( combinedName != null ) ((InvDatasetImpl) top).setName( combinedName);
//
//    // Finish up.
//    ((InvCatalogImpl) cat).finish();
//
//    return( cat);
//  }

//  private class MyFileFilter implements FileFilter
//  {
//    private String matchPattern;
//    private java.util.regex.Pattern pattern;
//    private java.util.regex.Matcher matcher;
//    MyFileFilter( String matchPattern )
//    {
//      StringBuffer tmpSb = new StringBuffer( matchPattern );
//      int index = tmpSb.indexOf( "*");
//      while ( index != -1)
//      {
//        tmpSb.replace( index, index+1, ".*");
//
//        index = tmpSb.indexOf( "*", index+2);
//      }
//      this.matchPattern = tmpSb.toString();
//      this.pattern = java.util.regex.Pattern.compile( this.matchPattern);
//    }
//    public boolean accept( File file )
//    {
//      if ( !file.isDirectory() ) return ( false );
//      this.matcher = this.pattern.matcher( file.getName());
//      if ( this.matcher.matches()) return( true);
//      return ( false );
//    }
//
//  }
}


/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.nc2.util.cache;

import ucar.nc2.util.Misc;

import javax.annotation.concurrent.Immutable;
import java.util.Arrays;
import java.util.Formatter;

/**
 * integer array in which we want to quickly find the index for a given value.
 * optimize for the case of 1) constant, 2) sequential, 3) sorted
 *
 * @author caron
 * @since 11/4/2014
 */
@Immutable
public class SmartArrayInt {
  private final int[] raw;
  private final int start;
  private final boolean isSequential;   // if elem[i] = constant + i; LOOK could generalize to strided
  private final boolean isConstant;   // if elem[i] = constant
  private final boolean isSorted;    // if elements are sorted, can use a binary search
  private final int n;

  public SmartArrayInt(int[] raw) {
    this.n = raw.length;
    if (raw.length == 0) {
      this.start = -1;
      this.isConstant = true;
      this.isSequential = false;
      this.isSorted = false;
      this.raw = null;
      return;
    }
    boolean isC = true;
    boolean isSeq = true;
    boolean isSort = true;
    this.start = raw[0];
    for (int i=0; i<raw.length; i++) {
      if (raw[i] != start+i) isSeq = false;
      if (raw[i] != start) isC = false;
      if (i>0 && raw[i] < raw[i-1]) isSort = false;
    }

    this.raw = (!isSeq && !isC) ? raw : null;
    this.isSequential = isSeq;
    this.isConstant = isC;
    this.isSorted = isSort;
  }

  public int get(int idx) {
    if (isConstant) return start;
    if (isSequential) return start+idx;
    return raw[idx];
  }

  public int getN() {
    return n;
  }

  public void show(Formatter f) {
    if (isConstant) f.format("isConstant=%d",start);
    else if (isSequential) f.format("isSequential start=%d",start);
    else {
      f.format("isSorted=%s ", isSorted);
      Misc.showInts(raw, f);
    }
  }

  /**
   * Find which index holds the value want
   * @param want  value wanted
   * @return < 0 if not found, else the index. If duplicates, then return any match
   */
  public int findIdx(int want) {
    if (isConstant) return (want == start)  ? 0 : -1;
    if (isSequential) return want - start;
    if (isSorted) {
      return Arrays.binarySearch(raw, want);
    }
    // linear search
    for (int i=0; i<raw.length; i++)
      if (raw[i] == want) return i;
    return -1;
  }

}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
// $Id

package thredds.cataloggen.config;

import thredds.catalog.MetadataConverterIF;
import thredds.catalog.InvDataset;
import thredds.catalog.ServiceType;

import org.jdom2.*;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.*;
import org.jdom2.output.*;

import java.util.ArrayList;
import java.util.Iterator;
import java.net.URI;
import java.io.IOException;

/**
 *
Title: Catalog Generator

 *
Description: Tool for generating THREDDS catalogs.

 *
Copyright: Copyright (c) 2001

 *
Company: UCAR/Unidata

 * @author Ethan Davis
 * @version 1.0
 */

public class CatGenConfigMetadataFactory
        implements MetadataConverterIF
{
  //private static Log log = LogFactory.getLog( CatGenConfigMetadataFactory.class );
  static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CatGenConfigMetadataFactory.class);

  private static boolean showParsedXML = false, debug = false;
  private static final Namespace CATALOG_GEN_CONFIG_NAMESPACE_0_5 =
          Namespace.getNamespace( CatalogGenConfig.CATALOG_GEN_CONFIG_NAMESPACE_URI_0_5 );
  //private DOMBuilder builder = new DOMBuilder();

  public CatGenConfigMetadataFactory()
  {
    log.debug( "CatGenConfigMetadataFactory(): .");
  }

 /**
  * Create an InvMetadata content object from an XML document at a named URL.
  * The content object is an ArrayList of CatalogGenConfig instances.
  *
  * @param dataset - the containing dataset
  * @param urlString - the URL where the XML doc is located.
  * @return
  * @throws java.net.MalformedURLException if given URL is malformed.
  * @throws java.io.IOException if problems reading from given URL
  */
  private Object readMetadataContentFromURL( InvDataset dataset, String urlString)
    throws java.net.MalformedURLException, java.io.IOException
  {
    // @todo This isn't used anywhere. Remove?
    Document doc;
    try
    {
      SAXBuilder builder = new SAXBuilder( true);
      doc = builder.build( urlString);
    } catch ( JDOMException e)
    {
      log.error( "CatGenConfigMetadataFactory parsing error= \n" + e.getMessage());
      throw new java.io.IOException( "CatGenConfigMetadataFactory parsing error= " + e.getMessage());
    }

    if (showParsedXML)
    {
      XMLOutputter xmlOut = new XMLOutputter( Format.getPrettyFormat() );
      System.out.println ("*** catalog/showParsedXML = \n"+xmlOut.outputString(doc)+"\n*******");
    }

    return( readMetadataContentJdom( dataset, doc.getRootElement()));
  }

  /**
   * Create an InvMetadata content object from an org.w3c.dom.Element.
   * The content object is an ArrayList of CatalogGenConfig instances.
   *
   * @param dataset - the containing dataset
   * @param mdataElement - the metadata element as an org.w3c.dom.Element
   * @return an object representing the metadata which is an ArrayList of CatalogGenConfig instances.
   */
  public Object readMetadataContent( InvDataset dataset, org.jdom2.Element mdataElement )
  {
    log.debug( "readMetadataContent(): ." );

    // convert to JDOM element
    //Element mdataElement = builder.build( mdataDomElement );
    return readMetadataContentJdom( dataset, mdataElement );
  }

  public Object readMetadataContentFromURL( InvDataset dataset, URI uri ) throws IOException
  {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  /**
   * Serialize the InvMetadata content object to a org.w3c.dom.Element
   *
   * @param mdataJdomElement - add content to this org.w3c.dom.Element
   * @param contentObject - the content model
   */
  public void addMetadataContent( org.jdom2.Element mdataJdomElement, Object contentObject )
  {
    // convert to JDOM element
    //Element mdataJdomElement = builder.build( mdataElement );

    ArrayList catGenConfigList = (ArrayList) contentObject;
    Iterator iter = catGenConfigList.iterator();
    while ( iter.hasNext())
    {
      CatalogGenConfig cgc = (CatalogGenConfig) iter.next();
      mdataJdomElement.addContent( createCatGenConfigElement( cgc));
    }
  }

  /** Validate the content object. */
  public boolean validateMetadataContent( Object contentObject, StringBuilder out)
  {
    boolean ok = true;
    ArrayList catGenConfigList = (ArrayList) contentObject;
    Iterator iter = catGenConfigList.iterator();
    while ( iter.hasNext()) {
      CatalogGenConfig catGenConf = (CatalogGenConfig) iter.next();
      ok &= catGenConf.validate( out);
    }
    return ok;

  }

  //---------------------------------------------------------------------------
  // Private methods to support readMetadataContent( InvDataset, org.w3c.dom.Element).

  /**
   * Given a metadata JDOM element, return metadata content object
   * (ArrayList of CatalogGenConfig).
   *

   * From the given metadata JDOM element, build (and return) an
   * ArrayList of CatalogGenConfig instances.
   */
  private Object readMetadataContentJdom( InvDataset dataset, Element mdataElement )
  {
    Namespace catGenConfigNamespace = null;
    ArrayList catGenConfigList = new ArrayList();
    // Get the "catalogGenConfig" children elements with
    // CatalogGenConfig namespace first and then with THREDDS namespace.
   Iterator iter = mdataElement.getChildren( "catalogGenConfig", CATALOG_GEN_CONFIG_NAMESPACE_0_5 ).iterator();
    if ( ! iter.hasNext())
      iter = mdataElement.getChildren( "catalogGenConfig", mdataElement.getNamespace() ).iterator();

    while ( iter.hasNext() )
    {
      Element catGenConfigElement = (Element) iter.next();
      if ( debug )
      {
        log.debug( "readMetadataContent=" + catGenConfigElement);
      }
      catGenConfigList.add( readCatGenConfigElement( dataset, catGenConfigElement ) );
    }
    return ( catGenConfigList );
  }

  /** Return a CatalogGenConfig when given a 'catalogGenConfig' JDOM element. */
  private CatalogGenConfig readCatGenConfigElement(
    InvDataset parentDataset, Element catGenConfElement)
  {

    String type = catGenConfElement.getAttributeValue("type");

    CatalogGenConfig catGenConf = new CatalogGenConfig( parentDataset, type);

    // get any datasetSource elements
    java.util.List list = catGenConfElement.getChildren( "datasetSource", catGenConfElement.getNamespace() );
    for (int i=0; i< list.size(); i++)
    {
      Element dsSourceElement = (Element) list.get(i);

      catGenConf.setDatasetSource( readDatasetSourceElement( parentDataset,
                                                             dsSourceElement));
    }

    // @todo Start only allowing datasetSource elements in catalogGenConfig elements.
//    // get any datasetNamer elements
//    list = catGenConfElement.getChildren( "datasetNamer", catGenConfElement.getNamespace() );
//    for (int i=0; i< list.size(); i++)
//    {
//      Element dsNamerElement = (Element) list.get(i);
//
//      catGenConf.addDatasetNamer( readDatasetNamerElement( parentDataset,
//                                                           dsNamerElement));
//    }

    return( catGenConf);
  }

  /** Return a DatasetSource when given a 'datasetSource' JDOM element. */
  private DatasetSource readDatasetSourceElement( InvDataset parentDataset,
                                                  Element dsSourceElement)
  {
    String name = dsSourceElement.getAttributeValue( "name");
    String type = dsSourceElement.getAttributeValue( "type");
    String structure = dsSourceElement.getAttributeValue( "structure");
    String accessPoint = dsSourceElement.getAttributeValue( "accessPoint");
    String createCatalogRefs = dsSourceElement.getAttributeValue( "createCatalogRefs");

    // get the resultService element
    Element resultServiceElement = dsSourceElement.getChild( "resultService", dsSourceElement.getNamespace() );
    ResultService resultService = readResultServiceElement( parentDataset,
                                                         resultServiceElement);

    DatasetSource dsSource = DatasetSource.newDatasetSource( name,
                                                    DatasetSourceType.getType( type),
                                                    DatasetSourceStructure.getStructure( structure),
                                                    accessPoint, resultService);
    if ( createCatalogRefs != null)
    {
      dsSource.setCreateCatalogRefs( Boolean.valueOf( createCatalogRefs).booleanValue());
    }

    // get any datasetNamer elements
    java.util.List list = dsSourceElement.getChildren( "datasetNamer", dsSourceElement.getNamespace() );
    for (int i=0; i< list.size(); i++)
    {
      Element dsNamerElement = (Element) list.get(i);

      dsSource.addDatasetNamer( readDatasetNamerElement( parentDataset,
                                                         dsNamerElement));
    }

    // get any datasetFilter elements
    list = dsSourceElement.getChildren( "datasetFilter", dsSourceElement.getNamespace() );
    for (int i=0; i< list.size(); i++)
    {
      Element dsFilterElement = (Element) list.get(i);

      dsSource.addDatasetFilter( readDatasetFilterElement( dsSource,
                                                           dsFilterElement));
    }

    return( dsSource);
  }

  /** Return a DatasetNamer when given a 'datasetNamer' JDOM element. */
  private DatasetNamer readDatasetNamerElement( InvDataset parentDataset,
                                                 Element dsNamerElement)
  {
    String name = dsNamerElement.getAttributeValue( "name");
    String addLevel = dsNamerElement.getAttributeValue( "addLevel");
    String type = dsNamerElement.getAttributeValue( "type");
    String matchPattern = dsNamerElement.getAttributeValue( "matchPattern");
    String substitutePattern = dsNamerElement.getAttributeValue( "substitutePattern");
    String attribContainer = dsNamerElement.getAttributeValue( "attribContainer");
    String attribName = dsNamerElement.getAttributeValue( "attribName");

    DatasetNamer dsNamer = new DatasetNamer( parentDataset,
                                             name, addLevel, type,
                                             matchPattern, substitutePattern,
                                             attribContainer, attribName);

    return( dsNamer);
  }

  /** Return a DatasetFilter when given a 'datasetFilter' JDOM element. */
  private DatasetFilter readDatasetFilterElement( DatasetSource parentDatasetSource,
                                                  Element dsFilterElement)
  {
    String name = dsFilterElement.getAttributeValue( "name");
    String type = dsFilterElement.getAttributeValue( "type");
    String matchPattern = dsFilterElement.getAttributeValue( "matchPattern");

    DatasetFilter dsFilter = new DatasetFilter( parentDatasetSource,
                                                name, DatasetFilter.Type.getType( type), matchPattern);
    String matchPatternTarget = dsFilterElement.getAttributeValue( "matchPatternTarget");
    dsFilter.setMatchPatternTarget( matchPatternTarget);

    if ( dsFilterElement.getAttributeValue( "applyToCollectionDatasets") != null)
    {
      boolean applyToCollectionDatasets = Boolean.valueOf( dsFilterElement.getAttributeValue( "applyToCollectionDatasets")).booleanValue();
      dsFilter.setApplyToCollectionDatasets( applyToCollectionDatasets);
    }
    if ( dsFilterElement.getAttributeValue( "applyToAtomicDatasets") != null)
    {
      boolean applyToAtomicDatasets = Boolean.valueOf( dsFilterElement.getAttributeValue( "applyToAtomicDatasets")).booleanValue();
      dsFilter.setApplyToAtomicDatasets( applyToAtomicDatasets);
    }
    if ( dsFilterElement.getAttributeValue( "rejectMatchingDatasets") != null)
    {
      boolean rejectMatchingDatasets = Boolean.valueOf( dsFilterElement.getAttributeValue( "rejectMatchingDatasets")).booleanValue();
      dsFilter.setRejectMatchingDatasets( rejectMatchingDatasets);
    }

    return( dsFilter);
  }

  /** Return a ResultService when given a 'resultService' JDOM element. */
  private ResultService readResultServiceElement( InvDataset parentDataset,
                                                  Element resultServiceElement)
  {
    String name = resultServiceElement.getAttributeValue( "name");
    String serviceType = resultServiceElement.getAttributeValue( "serviceType");
    String base = resultServiceElement.getAttributeValue( "base");
    String suffix = resultServiceElement.getAttributeValue( "suffix");
    String accessPointHeader =
      resultServiceElement.getAttributeValue( "accessPointHeader");

    return( new ResultService( name, ServiceType.getType( serviceType), base, suffix,
                               accessPointHeader));
  }

  //---------------------------------------------------------------------------
  // Private methods to support addMetadataContent()

  /** Create a 'catalogGenConfig' JDOM element */
  private org.jdom2.Element createCatGenConfigElement( CatalogGenConfig cgc)
  {
    // @todo Need to deal with the 0.6 and 1.0 namespaces.
    Element cgcElem = new Element("catalogGenConfig", CATALOG_GEN_CONFIG_NAMESPACE_0_5);
    if ( cgc != null)
    {
      if ( cgc.getType() != null)
      {
        cgcElem.setAttribute( "type", cgc.getType().toString());
      }

      // Add 'datasetSource' element
      DatasetSource dsSource = cgc.getDatasetSource();
      cgcElem.addContent( createDatasetSourceElement( dsSource));

    }

    return( cgcElem);
  }

  /** Create a 'DatasetSource' JDOM element */
  private org.jdom2.Element createDatasetSourceElement( DatasetSource dsSource)
  {
    Element dssElem = new Element("datasetSource", CATALOG_GEN_CONFIG_NAMESPACE_0_5);
    if ( dsSource != null)
    {
      // Add 'name' attribute.
      if ( dsSource.getName() != null)
      {
        dssElem.setAttribute( "name", dsSource.getName());
      }

      // Add 'type' attribute.
      if ( dsSource.getType() != null)
      {
        dssElem.setAttribute( "type", dsSource.getType().toString());
      }

      // Add 'structure' attribute.
      if ( dsSource.getStructure() != null)
      {
        dssElem.setAttribute( "structure", dsSource.getStructure().toString());
      }

      // Add 'accessPoint' attribute.
      if ( dsSource.getAccessPoint() != null)
      {
        dssElem.setAttribute( "accessPoint", dsSource.getAccessPoint());
      }

      // Add 'createCatalogRefs' attribute.
      dssElem.setAttribute( "createCatalogRefs", Boolean.toString(  dsSource.isCreateCatalogRefs()));

      // Add 'resultService' element
      ResultService rs = dsSource.getResultService();
      dssElem.addContent( createResultServiceElement( rs));

      // Add 'datasetNamer' elements
      java.util.List list = dsSource.getDatasetNamerList();
      for ( int j=0; j < list.size(); j++)
      {
        DatasetNamer dsNamer = (DatasetNamer) list.get(j);
        dssElem.addContent( createDatasetNamerElement( dsNamer));
      }

      // Add 'datasetFilter' elements
      list = dsSource.getDatasetFilterList();
      for ( int j=0; j < list.size(); j++)
      {
        DatasetFilter dsFilter = (DatasetFilter) list.get(j);
        dssElem.addContent( createDatasetFilterElement( dsFilter));
      }
    }

    return( dssElem);
  }

  /** Create a 'DatasetNamer' JDOM element */
  private org.jdom2.Element createDatasetNamerElement( DatasetNamer dsNamer)
  {
    Element dsnElem = new Element("datasetNamer", CATALOG_GEN_CONFIG_NAMESPACE_0_5);
    if ( dsNamer != null)
    {
      // Add 'name' attribute.
      if ( dsNamer.getName() != null)
      {
        dsnElem.setAttribute( "name", dsNamer.getName());
      }

      // Add 'addLevel' attribute.
      dsnElem.setAttribute( "addLevel", Boolean.toString( dsNamer.getAddLevel()));

      // Add 'type' attribute.
      if ( dsNamer.getType() != null)
      {
        dsnElem.setAttribute( "type", dsNamer.getType().toString());
      }

      // Add 'matchPattern' attribute.
      if ( dsNamer.getMatchPattern() != null)
      {
        dsnElem.setAttribute( "matchPattern", dsNamer.getMatchPattern());
      }

      // Add 'subsitutePattern' attribute.
      if ( dsNamer.getSubstitutePattern() != null)
      {
        dsnElem.setAttribute( "substitutePattern", dsNamer.getSubstitutePattern());
      }

      // Add 'attribContainer' attribute.
      if ( dsNamer.getAttribContainer() != null)
      {
        dsnElem.setAttribute( "attribContainer", dsNamer.getAttribContainer());
      }

      // Add 'attribName' attribute.
      if ( dsNamer.getAttribName() != null)
      {
        dsnElem.setAttribute( "attribName", dsNamer.getAttribName());
      }

    }

    return( dsnElem);
  }

  /** Create a 'DatasetFilter' JDOM element */
  private org.jdom2.Element createDatasetFilterElement( DatasetFilter dsFilter)
  {
    Element dsfElem = new Element("datasetFilter", CATALOG_GEN_CONFIG_NAMESPACE_0_5);
    if ( dsFilter != null)
    {
      // Add 'name' attribute.
      if ( dsFilter.getName() != null)
      {
        dsfElem.setAttribute( "name", dsFilter.getName());
      }

      // Add 'type' attribute.
      if ( dsFilter.getType() != null)
      {
        dsfElem.setAttribute( "type", dsFilter.getType().toString());
      }

      // Add 'matchPattern' attribute.
      if ( dsFilter.getMatchPattern() != null)
      {
        dsfElem.setAttribute( "matchPattern", dsFilter.getMatchPattern());
      }

      // Add 'matchPatternTarget' attribute.
      if ( dsFilter.getMatchPatternTarget() != null)
      {
        dsfElem.setAttribute( "matchPatternTarget", dsFilter.getMatchPatternTarget());
      }

      // Add 'applyToCollectionDatasets' attribute.
      dsfElem.setAttribute( "applyToCollectionDatasets", String.valueOf( dsFilter.isApplyToCollectionDatasets()));

      // Add 'applyToAtomicDatasets' attribute.
      dsfElem.setAttribute( "applyToAtomicDatasets", String.valueOf( dsFilter.isApplyToAtomicDatasets()));

      // Add 'rejectMatchingDatasets' attribute.
      dsfElem.setAttribute( "rejectMatchingDatasets", String.valueOf( dsFilter.isRejectMatchingDatasets()));

    }

    return( dsfElem);
  }

  /** Create a 'ResultService' JDOM element */
  private org.jdom2.Element createResultServiceElement( ResultService resultService)
  {
    Element rsElem = new Element("resultService", CATALOG_GEN_CONFIG_NAMESPACE_0_5);
    if ( resultService != null)
    {
      // Add 'name' attribute.
      if ( resultService.getName() != null)
      {
        rsElem.setAttribute( "name", resultService.getName());
      }

      // Add 'serviceType' attribute.
      if ( resultService.getServiceType() != null)
      {
        rsElem.setAttribute( "serviceType", resultService.getServiceType().toString());
      }

      // Add 'base' attribute.
      if ( resultService.getBase() != null)
      {
        rsElem.setAttribute( "base", resultService.getBase());
      }

      // Add 'suffix' attribute.
      if ( resultService.getSuffix() != null)
      {
        rsElem.setAttribute( "suffix", resultService.getSuffix());
      }

      // Add 'accessPointHeader' attribute.
      if ( resultService.getAccessPointHeader() != null)
      {
        rsElem.setAttribute( "accessPointHeader", resultService.getAccessPointHeader());
      }
    }

    return( rsElem);
  }
}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.util.prefs.ui;

import java.util.prefs.Preferences;
import java.util.prefs.BackingStoreException;

import javax.swing.JMenu;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.event.*;

/**
 * Provides static methods for managing persistent lists of debugging flags that
 * can be set by the user at runtime. A new debug flag is added to the Store
 * whenever isSet() is called and the flag name does not already exist.
 * The flags can be set dynamically at runtime through a JMenu, with the
 * name of the flag used for the menu label, and the / separator indicating menu
 * levels. Note that this class does not depend on using PreferencesExt,
 * and so could also be used with the default Preferences implementations.
 *
 *
 To use, make sure that you call setStore() before anything else; it is a
 * good idea to use a separate node to avoid name collisions:

 Debug.setStore(prefs.node("/debugFlags"));

 *
 To allow user control at runtime, add a JMenu instance variable to your main menu,
 *  which constructs itself whenever it is called using the current set of flags, e.g.:

  JRootPane rootPane = getRootPaneContainer().getRootPane();
  JMenuBar mb = new JMenuBar();
  rootPane.setJMenuBar(mb);
  JMenu sysMenu = new JMenu("System");
  mb.add(sysMenu);

  JMenu debugMenu = (JMenu) sysMenu.add(new JMenu("Debug"));
  debugMenu.addMenuListener( new MenuListener() {
    public void menuSelected(MenuEvent e) { Debug.constructMenu(debugMenu);}
    public void menuDeselected(MenuEvent e) {}
    public void menuCanceled(MenuEvent e) {}
  });
  sysMenu.add(debugMenu);

 *
 * Then in your application code, check to see if a debug flag is turned on, for example:

   if (Debug.isSet("util/configure/stuff")) do_good_stuff();

 *
 *
 The first time this code is called, this flag will be added to the Store and
 * to the menu (with a value of false). The user can then turn it on using the menu system.
 * This allows you to add fine-grained debugging control without having to manage
 * a separate list of flags. Since these are all static methods, you do not have to pass
 * around a global Debug object. In this example, the top menu item to be added will be c
 * alled "util" the second item called "configure", and the flag itself will be called
 * "stuff". The menu may nest to arbitrary depth. Similarly, a substore of the main
 * debug Store (the one passed into setStore()) is created called "util", which has a
 * substore called "configure", which has a boolean value whose key is "stuff".
 * Thus name collisions should be easy to avoid.
 *
 * @see java.util.prefs.Preferences
 * @author John Caron
 */

public class Debug {
  static private Preferences store = null;
  static private boolean debug = false, debugEvents = false;

  /** Set the persistent data. You must call this before any other call. */
  static public void setStore(Preferences debugStore) {
    store = debugStore;
  }

  /** Return the value of the named flag. If it doesnt exist, it will be added
   *  to the store and the menu with a value of "false".
   */
  static public boolean isSet(String flagName) {
    if (store == null) return false;

    NamePart np = partit( flagName);
    if (debug) {
      try {
        if ((np.storeName.length() > 0) && !store.nodeExists(np.storeName))
          System.out.println("Debug.isSet create node = "+ flagName+" "+np);
        else if (null == store.node(np.storeName).get( np.keyName, null))
          System.out.println("Debug.isSet create flag = "+ flagName+" "+np);
      } catch (BackingStoreException e) { }
    }
    // add it if it doesnt already exist
    boolean value =  store.node(np.storeName).getBoolean( np.keyName, false);
    store.node(np.storeName).putBoolean( np.keyName, value);
    return value;
  }

  /** Set the value of the named flag. If it doesnt exist, it will be added
   *  to the store and the menu.
   */
  static public void set(String flagName, boolean value) {
    NamePart np = partit( flagName);
    if (debug) {
      try {
        if ((np.storeName.length() > 0) && !store.nodeExists(np.storeName))
          System.out.println("Debug.set create node = "+ flagName+" "+np);
        else if (null == store.node(np.storeName).get( np.keyName, null))
          System.out.println("Debug.set create flag = "+ flagName+" "+np);
      } catch (BackingStoreException e) { }
    }
    store.node(np.storeName).putBoolean( np.keyName, value);
  }

  /** Clear all flags (set to false).
   */
  static public void removeAll() {
    try {
      removeAll(store, false);
    } catch (BackingStoreException ex) {
      throw new RuntimeException(ex);
    }
  }

  static private void removeAll(Preferences prefs, boolean delete) throws BackingStoreException {

    String[] kidName = prefs.childrenNames();
    for (String aKidName : kidName) {
      Preferences pkid = prefs.node(aKidName);
      removeAll(pkid, true);
    }

    if (delete)
      prefs.removeNode();
    else
      prefs.clear();
  }

  /* static public void clear() {
   map = new TreeMap();
  } */

  /** Construct cascading pull-aside menus using the values of the debug flags
   *  in the Preferences object.
   *  @param topMenu attach the menus as children of this one.
   */
  static public void constructMenu(JMenu topMenu) {
    if (debug) System.out.println("Debug.constructMenu ");

    if (topMenu.getItemCount() > 0)
      topMenu.removeAll();

    try {
      addToMenu( topMenu, store); // recursive
    } catch (BackingStoreException e) { }

    topMenu.revalidate();
  }

    // recursive menu adding
  static private void addToMenu( JMenu menu, Preferences prefs) throws BackingStoreException {
    if (debug) System.out.println(" addMenu "+ prefs.name());

    String[] keys = prefs.keys();
    for (String key : keys) {
      boolean bval = prefs.getBoolean(key, false);
      String fullname = prefs.absolutePath() + "/" + key;
      menu.add(new DebugMenuItem(fullname, key, bval)); // menu leaf
      if (debug) System.out.println("   leaf= <" + key + "><" + fullname + ">");
    }

    String[] kidName = prefs.childrenNames();
    for (String aKidName : kidName) {
      Preferences pkid = prefs.node(aKidName);
      JMenu subMenu = new JMenu(pkid.name());
      menu.add(subMenu);
      addToMenu(subMenu, pkid);
    }
  }

  static private NamePart partit( String name) {
    NamePart np = new NamePart();

    //name = name.replace('.', '/');
    int pos = name.lastIndexOf('/');
    if (pos >= 0) {
      np.storeName = name.substring(0, pos);
      np.keyName = name.substring(pos+1);
      if (np.storeName.startsWith("/"))
        np.storeName = np.storeName.substring(1);
    } else
       np.keyName = name;

    return np;
  }

  private static class NamePart{
    String storeName = "", keyName = "";
    public String toString() { return "<"+storeName+"> <"+keyName+">"; }
  }

  private static class DebugMenuItem extends JCheckBoxMenuItem {
    private String fullname;

    DebugMenuItem( String foolName, String menuName, boolean val) {
      super(menuName, val);

      fullname = foolName;
      if (fullname.startsWith("/Debug/")) {
        fullname = fullname.substring(7);
      }

      addChangeListener(e -> {
          if (debugEvents) { System.out.println("DebugMenuItem "+getText()+" "+getState()); }
          Debug.set(fullname, getState());
      });
    }
  }

}