/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.nc2.ft2.coverage;

import ucar.ma2.Index;
import ucar.ma2.RangeIterator;
import ucar.nc2.constants.AxisType;
import ucar.nc2.time.CalendarDate;

import javax.annotation.concurrent.Immutable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * An iterator over the coordinates in a set of CoverageCoordAxis.
 * The independent axes are used to iterate.
 * Attached dependent axes are used to generate values also. Two cases handled so far:
 *   1) ConstantForecast: runtime (ind), timeOffset (dep), time (scalar)
 *   1) Best: time (ind), runtime (dep)
 *
 *  Grib: If theres a time offset, then there must be a runtime coordinate, and the time offset is reletive to that.
 *  LOOK only used by Grib
 */
@Immutable
public class CoordsSet implements Iterable<SubsetParams> {
  /* public static final String runDate = "runDate";                 // CalendarDate
  public static final String timeOffsetCoord = "timeOffsetCoord";   // Double or double[] (intv)
  public static final String timeOffsetDate = "timeOffsetDate";   // CalendarDate (validation only)
  // public static final String timeCoord = "timeCoord";   // Double or double[] (intv)
  public static final String vertCoord = "vertCoord";   // Double or double[] (intv)
  public static final String ensCoord = "ensCoord";   // Double */

  public static CoordsSet factory(boolean constantForecast, List<CoverageCoordAxis> axes) {
    return new CoordsSet(constantForecast, axes);
  }

  ///////////////////////////////////////////////////////
  private final boolean constantForecast;
  private final List<CoverageCoordAxis> axes;     // all axes
  private final int[] shape;                      // only independent

  private CoordsSet(boolean constantForecast, List<CoverageCoordAxis> axes) {
    this.constantForecast = constantForecast;
    List<CoverageCoordAxis1D> indAxes = new ArrayList<>();
    int rank = 0;

    for (CoverageCoordAxis axis : axes) {
      if (axis.getDependenceType() != CoverageCoordAxis.DependenceType.dependent)  // independent or scalar
        indAxes.add( (CoverageCoordAxis1D) axis);
      if (axis.getDependenceType() == CoverageCoordAxis.DependenceType.independent)
        rank++;
    }
    this.axes = axes;

    this.shape = new int[rank];
    int count = 0;
    for (CoverageCoordAxis1D axis : indAxes) {
      if (axis.getDependenceType() == CoverageCoordAxis.DependenceType.independent)
        shape[count++] = axis.getNcoords();
    }
  }

  public int[] getShape() {
    return shape;
  }

  public int[] getShape(RangeIterator y, RangeIterator x) {
    int[] result = new int[getRank() + 2];
    System.arraycopy(shape, 0, result, 0, shape.length);
    result[shape.length] = y.length();
    result[shape.length+1] = x.length();
    return result;
  }

  public int getRank() {
    return shape.length;
  }

  @Override
  public Iterator<SubsetParams> iterator() {
    return new CoordIterator();
  }

  private class CoordIterator implements Iterator<SubsetParams> {
    private int[] odo = new int[getRank()];
    private int[] shape = getShape();
    private long done, total;

    CoordIterator() {
      done = 0;
      total = Index.computeSize(shape); // total number of elements
    }

    public boolean hasNext() {
      return done < total;
    }

    public SubsetParams next() {
      SubsetParams next = currentElement();
      done++;
      if (done < total) incr(); // increment for next call
      return next;
    }

    private void incr() {
      int digit = getRank() - 1;
      while (digit >= 0) {
        odo[digit]++;
        if (odo[digit] < shape[digit])
          break; // normal exit

        // else, carry to next digit in the odometer
        odo[digit] = 0;
        digit--;
      }
    }

    private SubsetParams currentElement() {
      SubsetParams result = new SubsetParams();
      int odoIndex = 0;
      CalendarDate runtime = null;
      for (CoverageCoordAxis axis : axes) {
        if (axis.getDependenceType() == CoverageCoordAxis.DependenceType.dependent) continue;
        CoverageCoordAxis1D axis1D = (CoverageCoordAxis1D) axis;

        int coordIdx = (axis.getDependenceType() == CoverageCoordAxis.DependenceType.scalar) ? 0 : odo[odoIndex];
        Object coord = axis1D.getCoordObject(coordIdx); // CalendarDate (runtime), Double or double{} (interval)

        if (axis.getAxisType() == AxisType.RunTime) {
          runtime = (CalendarDate) coord;
          result.setRunTime(runtime);

          if (constantForecast) {
            CoverageCoordAxis1D timeOffsetCF = findDependent(axis, AxisType.TimeOffset);
            if (timeOffsetCF != null) {
              addAdjustedTimeCoords(result, timeOffsetCF, coordIdx, runtime);
            }
          }

        } else if (axis.getAxisType() == AxisType.Time) {
          CoverageCoordAxis1D runtimeForBest = findDependent(axis, AxisType.RunTime);
          if (runtimeForBest != null) {
            runtime = (CalendarDate) runtimeForBest.getCoordObject(coordIdx); // CalendarDate
            result.setRunTime(runtime);
          }
          assert runtime != null;
          addAdjustedTimeCoords(result, axis1D, coordIdx, runtime);
        }

        else if (axis.getAxisType().isVert()) {
          if (coord instanceof Double)
            result.setVertCoord( (Double) coord);
          else if (coord instanceof double[])
            result.setVertCoordIntv((double[]) coord);
          else
            throw new IllegalStateException("unknow vert coord type "+coord.getClass().getName());
        }

        else if (axis.getAxisType() == AxisType.Ensemble)
          result.setEnsCoord((Double) coord);

        else if (!constantForecast && axis.getAxisType() == AxisType.TimeOffset) {
          if (coord instanceof Double)
            result.setTimeOffset( (Double) coord);
          else if (coord instanceof double[])
            result.setTimeOffsetIntv((double[]) coord);
          else
            throw new IllegalStateException("unknow time coord type "+coord.getClass().getName());

          double val = axis.isInterval() ? (axis1D.getCoordEdge1(coordIdx) + axis1D.getCoordEdge2(coordIdx)) / 2.0  : axis1D.getCoordMidpoint(coordIdx);
          assert runtime != null;
          result.set(SubsetParams.timeOffsetDate, axis.makeDateInTimeUnits(runtime, val)); // validation
          result.set(SubsetParams.timeOffsetUnit, axis.getCalendarDateUnit()); // validation
        }

        if (axis.getDependenceType() == CoverageCoordAxis.DependenceType.independent)
          odoIndex++;
      }
      return result;
    }

  }

  private void addAdjustedTimeCoords(SubsetParams result, CoverageCoordAxis1D axis, int coordIdx, CalendarDate runtime) {
    // this must be adjusted to be offset from the runtime.
    // adjust = end - start
    // axisCoordOffset + axis.reftime = offset + runtime
    // offset = axisCoordOffset + axis.reftime - runtime
    // offset = axisCoordOffset + adjust
    // offset = axisCoordOffset + end - start = axisCoordOffset + axis.reftime - runtime
    // therefore: end = reftime, start = runtime
    double adjust = axis.getOffsetInTimeUnits(runtime, axis.getRefDate());
    if (axis.isInterval()) {
      double[] adjustVal = new double[] {axis.getCoordEdge1(coordIdx)+adjust, axis.getCoordEdge2(coordIdx)+adjust};
      result.setTimeOffsetIntv(adjustVal);
      double mid = (adjustVal[0]+adjustVal[1]) / 2.0;
      result.set(SubsetParams.timeOffsetUnit, axis.makeDateInTimeUnits(runtime, mid)); // validation
      result.set(SubsetParams.timeOffsetUnit, axis.getCalendarDateUnit()); // validation
    } else {
      double adjustVal = axis.getCoordMidpoint(coordIdx) + adjust;
      result.setTimeOffset(adjustVal);
      result.set(SubsetParams.timeOffsetDate, axis.makeDateInTimeUnits(runtime, adjustVal)); // validation
      result.set(SubsetParams.timeOffsetUnit, axis.getCalendarDateUnit()); // validation
    }
  }

  // find the dependent axis that depend on independentAxis
  private CoverageCoordAxis1D  findDependent( CoverageCoordAxis independentAxis, AxisType axisType) {
    for (CoverageCoordAxis axis : axes) {
      if (axis.getDependenceType() == CoverageCoordAxis.DependenceType.dependent) {
        for (String axisName : axis.dependsOn) {
          if (axisName.equalsIgnoreCase(independentAxis.getName()) && axis.getAxisType() == axisType)
            return (CoverageCoordAxis1D) axis;
        }
      }
    }
    return null;
  }
}


/////////////////////////////////////////////////////////////////////////////
// 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.servers;

import java.io.*;
import java.util.Enumeration;

import opendap.dap.*;

/**
 * Holds a OPeNDAP Server Structure value.
 *
 * @author ndp
 * @version $Revision: 15901 $
 * @see BaseType
 */
public abstract class SDStructure extends DStructure implements ServerMethods, RelOps {
    private boolean Synthesized;
    private boolean ReadMe;

    /**
     * Constructs a new SDStructure.
     */
    public SDStructure() {
        super();
        Synthesized = false;
        ReadMe = false;
    }

    /**
     * Constructs a new SDStructure with name n.
     *
     * @param n the name of the variable.
     */
    public SDStructure(String n) {
        super(n);
        Synthesized = false;
        ReadMe = false;
    }

    /**
     * Write the variable's declaration in a C-style syntax. This
     * function is used to create textual representation of the Data
     * Descriptor Structure (DDS).  See The OPeNDAP User Manual for
     * information about this structure.
     *
     * @param os          The PrintWriter on which to print the
     *                    declaration.
     * @param space       Each line of the declaration will begin with the
     *                    characters in this string.  Usually used for leading spaces.
     * @param print_semi  a boolean value indicating whether to print a
     *                    semicolon at the end of the declaration.
     * @param constrained a boolean value indicating whether to print
     *                    the declartion dependent on the projection information. This
     *                    is only used by Server side code.
     * @see BaseType#printDecl(PrintWriter, String, boolean,boolean)
     */
    public void printDecl(PrintWriter os, String space,
                          boolean print_semi, boolean constrained) {

        // BEWARE! Since printDecl()is (multiple) overloaded in BaseType and
        // all of the different signatures of printDecl() in BaseType lead to
        // one signature, we must be careful to override that SAME signature
        // here. That way all calls to printDecl() for this object lead to
        // this implementation.

        // Also, since printDecl()is (multiple) overloaded in BaseType and
        // all of the different signatures of printDecl() in BaseType lead to
        // the signature we are overriding here, we MUST call the printDecl
        // with the SAME signature THROUGH the super class reference
        // (assuming we want the super class functionality). If we do
        // otherwise, we will create an infinte call loop. OOPS!

        if (constrained && !isProject())
            return;

        super.printDecl(os, space, print_semi, constrained);

    }

    /**
     * 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.
     *

     *
 Important Note

     * This method overrides the BaseType method of the same name and
     * type signature and it significantly changes the behavior for all versions
     * of printVal() for this type:
     *  All the various versions of printVal() will only
     * print a value, or a value with declaration, if the variable is
     * in the projection.
     *

     *
In other words, if a call to
     * isProject() for a particular variable returns
     * true then printVal() will print a value
     * (or a declaration and a value).
     *

     *
If isProject() for a particular variable returns
     * false then printVal() is basically a No-Op.
     *

     *

     *
     * @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.
     * @see BaseType#printVal(PrintWriter, String, boolean)
     * @see ServerMethods#isProject()
     */
    public void printVal(PrintWriter os, String space, boolean print_decl_p) {

        if (!isProject())
            return;

        if (print_decl_p) {
            printDecl(os, space, false, true);
            os.print(" = ");
        }

        boolean first = true;

        os.print("{ ");
        for (Enumeration e = vars.elements(); e.hasMoreElements();) {

            BaseType bt = (BaseType) e.nextElement();

            if (((ServerMethods) bt).isProject()) {

                if (!first)
                    os.print(", ");

                bt.printVal(os, "", false);
                first = false;

            }
        }
        os.print(" }");

        if (print_decl_p)
            os.println(";");
    }

    // --------------- Projection Interface

    /**
     * Set the state of this variable's projection. true means
     * that this variable is part of the current projection as defined by
     * the current constraint expression, otherwise the current projection
     * for this variable should be false.
     *
     * @param state true if the variable is part of the current
     *              projection, false otherwise.
     * @param all   If true, set the Project property of all the
     *              members (and their children, and so on).
     * @see CEEvaluator
     */
    @Override
    public void setProject(boolean state, boolean all) {
        super.setProject(state,all);
        if (all)
            for (Enumeration e = vars.elements(); e.hasMoreElements();) {
                ServerMethods sm = (ServerMethods) e.nextElement();
                sm.setProject(state, all);
            }
    }

// --------------- RelOps Interface

    /**
     * The RelOps interface defines how each type responds to relational
     * operators. Most (all?) types will not have sensible responses to all of
     * the relational operators (e.g. DStructure won't know how to match a regular
     * expression but DString will). For those operators that are nonsensical a
     * class should throw InvalidOperatorException.
     */

    public boolean equal(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("Equals (=) operator does not work with the type SDStructure!");
    }

    public boolean not_equal(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("Not Equals (!=) operator does not work with the type SDStructure!");
    }

    public boolean greater(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("Greater Than (>)operator does not work with the type SDStructure!");
    }

    public boolean greater_eql(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("GreaterThan or equals (<=) operator does not work with the type SDStructure!");
    }

    public boolean less(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("LessThan (<) operator does not work with the type SDStructure!");
    }

    public boolean less_eql(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("LessThan oe equals (<=) operator does not work with the type SDStructure!");
    }

    public boolean regexp(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("Regular Expression's don't work with the type SDStructure!");
    }

// --------------- FileIO Interface

    /**
     * Set the Synthesized property.
     *
     * @param state If true then the variable is considered a
     *              synthetic variable and no part of OPeNDAP will ever try to read it from a
     *              file, otherwise if false the variable is considered a
     *              normal variable whose value should be read using the
     *              read() method. By default this property is false.
     * @see #isSynthesized()
     * @see #read(String, Object)
     */
    public void setSynthesized(boolean state) {
        Synthesized = state;
    }

    /**
     * Get the value of the Synthesized property.
     *
     * @return true if this is a synthetic variable,
     *         false otherwise.
     */
    public boolean isSynthesized() {
        return (Synthesized);
    }

    /**
     * Set the Read property. A normal variable is read using the
     * read() method. Once read the Read property is
     * true. Use this function to manually set the property
     * value. By default this property is false.
     *
     * @param state true if the variable has been read,
     *              false otherwise.
     * @see #isRead()
     * @see #read(String, Object)
     */
    public void setRead(boolean state) {
        ReadMe = state;
    }

    /**
     * Get the value of the Read property.
     *
     * @return true if the variable has been read,
     *         false otherwise.
     * @see #read(String, Object)
     * @see #setRead(boolean)
     */
    public boolean isRead() {
        return (ReadMe);
    }

    /**
     * Read a value from the named dataset for this variable.
     *
     * @param datasetName String identifying the file or other data store
     *                    from which to read a vaue for this variable.
     * @param specialO    This Object is a goody that is used by Server implementations
     *                    to deliver important, and as yet unknown, stuff to the read method. If you
     *                    don't need it, make it a null.
     * @return true if more data remains to be read, otherwise
     *         false. This is an abtsract method that must be implemented
     *         as part of the installation/localization of a OPeNDAP server.
     * @throws IOException
     * @throws EOFException
     */
    public abstract boolean read(String datasetName, Object specialO) throws NoSuchVariableException, IOException, EOFException;

    /**
     * Server-side serialization for OPeNDAP variables (sub-classes of
     * BaseType).
     * This does not send the entire class as the Java Serializable
     * interface does, rather it sends only the binary data values. Other software
     * is responsible for sending variable type information (see DDS).
     *

     * Writes data to a DataOutputStream. This method is used
     * on the server side of the OPeNDAP client/server connection, and possibly
     * 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.
     * @see BaseType
     * @see DDS
     * @see ServerDDS
     */
    public void serialize(String dataset, DataOutputStream sink, CEEvaluator ce, Object specialO)
            throws NoSuchVariableException, DAP2ServerSideException, IOException {

        //System.out.println("Serializing Structure "+getName()+"   isRead(): "+isRead()+"  isProject(): " + isProject());

        if (!isRead())
            read(dataset, specialO);

        if (ce.evalClauses(specialO)) {
            for (Enumeration e = vars.elements(); e.hasMoreElements();) {
                ServerMethods sm = (ServerMethods) e.nextElement();

                if (sm.isProject())
                    sm.serialize(dataset, sink, ce, specialO);
            }
        }
    }

    /**
     * Write the variable's declaration in XML. This
     * function is used to create the XML representation of the Data
     * Descriptor Structure (DDS).  See The OPeNDAP User Manual for
     * information about this structure.
     *
     * @param constrained a boolean value indicating whether to print
     *                    the declartion dependent on the projection information. This
     *                    is only used by Server side code.
     * @see DDS
     */
    public void printXML(PrintWriter pw, String pad, boolean constrained) {

        // BEWARE! Since printXML()is (multiple) overloaded in BaseType
        // and all of the different signatures of printXML() in BaseType
        // lead to one signature, we must be careful to override that
        // SAME signature here. That way all calls to printDecl() for
        // this object lead to this implementation.

        // Also, since printXML()is (multiple) overloaded in BaseType
        // and all of the different signatures of printXML() in BaseType
        // lead to the signature we are overriding here, we MUST call
        // the printXML with the SAME signature THROUGH the super class
        // reference (assuming we want the super class functionality). If
        // we do otherwise, we will create an infinte call loop. OOPS!

        if (constrained && !isProject())
            return;

        super.printXML(pw, pad, constrained);
    }

}


/////////////////////////////////////////////////////////////////////////////
// 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.servers;

import java.io.*;
import java.util.Vector;
import java.util.Enumeration;

import opendap.dap.*;
import opendap.util.Debug;

/**
 * Holds a OPeNDAP Server Sequence value.
 *
 * @author ndp
 * @version $Revision: 15901 $
 * @see BaseType
 */

public abstract class SDSequence extends DSequence implements ServerMethods, RelOps {

    private boolean _Debug;

    private boolean Synthesized;
    private boolean ReadMe;

    /**
     * Constructs a new SDSequence.
     */
    public SDSequence() {
        super();
        Synthesized = false;
        ReadMe = false;
        _Debug = Debug.isSet("SDSequence");
    }

    /**
     * Constructs a new SDSequence with name n.
     *
     * @param n the name of the variable.
     */
    public SDSequence(String n) {
        super(n);
        Synthesized = false;
        ReadMe = false;

        _Debug = Debug.isSet("SDSequence");
    }

    /**
     * Get the row vector for into which to read a row os data for this sequence.
     * When serving sequence data to clients the prefered method is to read one row
     * of the sequence at a time in to this vector, evaluate the constraint expression
     * clauses on the current data, and then send it to the client if it satisfies
     * the constraint. The NOT recomended way is to read the ENTIRE sequence into memory
     * prior to sending it (that would be most inefficient).
     *
     * @return The base (row 0) row vector for this sequence.
     */

    public Vector getRowVector() throws NoSuchVariableException {

        if (getRowCount() == 0) {
            if (_Debug) System.out.println("This sequence has " + getRowCount() + " rows.");

            Vector rv = new Vector();

            for (int i = 0; i < elementCount(false); i++) {
                if (_Debug) System.out.println("Building variable " + i + ": " + getVar(i).getEncodedName());
                rv.add(getVar(i));
            }
            if (_Debug) System.out.println("Adding row to sequence...");

            addRow(rv);
        }

        return (getRow(0));
    }

    /**
     * Write the variable's declaration in a C-style syntax. This
     * function is used to create textual representation of the Data
     * Descriptor Structure (DDS).  See The OPeNDAP User Manual for
     * information about this structure.
     *
     * @param os          The PrintWriter on which to print the
     *                    declaration.
     * @param space       Each line of the declaration will begin with the
     *                    characters in this string.  Usually used for leading spaces.
     * @param print_semi  a boolean value indicating whether to print a
     *                    semicolon at the end of the declaration.
     * @param constrained a boolean value indicating whether to print
     *                    the declartion dependent on the projection information. This
     *                    is only used by Server side code.
     * @see BaseType#printDecl(PrintWriter, String, boolean,boolean)
     */
    public void printDecl(PrintWriter os, String space,
                          boolean print_semi, boolean constrained) {

        // BEWARE! Since printDecl()is (multiple) overloaded in BaseType
        // and all of the different signatures of printDecl() in BaseType
        // lead to one signature, we must be careful to override that
        // SAME signature here. That way all calls to printDecl() for
        // this object lead to this implementation.

        // Also, since printDecl()is (multiple) overloaded in BaseType and
        // all of the different signatures of printDecl() in BaseType lead to
        // the signature we are overriding here, we MUST call the printDecl
        // with the SAME signature THROUGH the super class reference
        // (assuming we want the super class functionality). If we do
        // otherwise, we will create an infinte call loop. OOPS!

        // If we are constrained, make sure some part of this thing is projected
        if (constrained && !isProject())
            return;

        super.printDecl(os, space, print_semi, constrained);

    }

    /**
     * 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.
     *

     *
 Important Note

     * This method overrides the BaseType method of the same name and
     * type signature and it significantly changes the behavior for all versions
     * of printVal() for this type:
     *  All the various versions of printVal() will only
     * print a value, or a value with declaration, if the variable is
     * in the projection.
     *

     *
In other words, if a call to
     * isProject() for a particular variable returns
     * true then printVal() will print a value
     * (or a declaration and a value).
     *

     *
If isProject() for a particular variable returns
     * false then printVal() is basically a No-Op.
     *

     *

     *
     * @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.
     * @see BaseType#printVal(PrintWriter, String, boolean)
     * @see ServerMethods#isProject()
     */
    public void printVal(PrintWriter os, String space, boolean print_decl_p) {

        if (!isProject())
            return;

        if (print_decl_p) {
            printDecl(os, space, false, true);
            os.print(" = ");
        }

        os.print("{ ");

        try {
            boolean firstPass = true;
            Vector v = getRowVector();
            for (Enumeration e2 = v.elements(); e2.hasMoreElements();) {
                // get next instance variable
                BaseType bt = (BaseType) e2.nextElement();

                if (((ServerMethods) bt).isProject()) {
                    if (!firstPass)
                        os.print(", ");
                    bt.printVal(os, "", false);
                    firstPass = false;
                }

            }
        }
        catch (NoSuchVariableException e) {
            os.println("Very Bad Things Happened When I Tried To Print " +
                    "A Row Of The Sequence: " + getEncodedName());
        }
        os.print(" }");

        if (print_decl_p)
            os.println(";");
    }

    // --------------- Projection Interface

    /**
     * Set the state of this variable's projection. true means
     * that this variable is part of the current projection as defined by
     * the current constraint expression, otherwise the current projection
     * for this variable should be false.
     *
     * @param state true if the variable is part of the current
     *              projection, false otherwise.
     * @param all   If true, set the Project property of all the
     *              members (and their children, and so on).
     * @see CEEvaluator
     */
    @Override
    public void setProject(boolean state, boolean all) {
        setProjected(state);
        if (all)
            for (Enumeration e = varTemplate.elements(); e.hasMoreElements();) {
                ServerMethods sm = (ServerMethods) e.nextElement();
                sm.setProject(state);
            }
    }

// --------------- RelOps Interface

    /**
     * The RelOps interface defines how each type responds to relational
     * operators. Most (all?) types will not have sensible responses to all of
     * the relational operators (e.g. DSequence won't know how to match a regular
     * expression but DString will). For those operators that are nonsensical a
     * class should throw InvalidOperatorException.
     */

    public boolean equal(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("Equals (=) operator does not work with the type SDSequence!");
    }

    public boolean not_equal(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("Not Equals (!=) operator does not work with the type SDSequence!");
    }

    public boolean greater(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("Greater Than (>)operator does not work with the type SDSequence!");
    }

    public boolean greater_eql(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("GreaterThan or equals (<=) operator does not work with the type SDSequence!");
    }

    public boolean less(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("LessThan (<) operator does not work with the type SDSequence!");
    }

    public boolean less_eql(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("LessThan oe equals (<=) operator does not work with the type SDSequence!");
    }

    public boolean regexp(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
        throw new InvalidOperatorException("Regular Expression's don't work with the type SDSequence!");
    }

// --------------- FileIO Interface

    /**
     * Set the Synthesized property.
     *
     * @param state If true then the variable is considered a
     *              synthetic variable and no part of OPeNDAP will ever try to read it from a
     *              file, otherwise if false the variable is considered a
     *              normal variable whose value should be read using the
     *              read() method. By default this property is false.
     * @see #isSynthesized()
     * @see #read(String, Object)
     */
    public void setSynthesized(boolean state) {
        Synthesized = state;
    }

    /**
     * Get the value of the Synthesized property.
     *
     * @return true if this is a synthetic variable,
     *         false otherwise.
     */
    public boolean isSynthesized() {
        return (Synthesized);
    }

    /**
     * Set the Read property. A normal variable is read using the
     * read() method. Once read the Read property is
     * true. Use this function to manually set the property
     * value. By default this property is false.
     *
     * @param state true if the variable has been read,
     *              false otherwise.
     * @see #isRead()
     * @see #read(String, Object)
     */
    public void setRead(boolean state) {
        ReadMe = state;

        //for (Enumeration e = varTemplate.elements();e.hasMoreElements();) {
        //  ServerMethods sm = (ServerMethods)e.nextElement();
        //System.out.println("Setting Read Flag for "+((BaseType)sm).getName()+" to "+state);
        //sm.setRead(state);
        //}

    }

    /**
     * Set the Read property. A normal variable is read using the
     * read() method. Once read the Read property is
     * true. Use this function to manually set the property
     * value. By default this property is false.
     *
     * @param state true if the variable has been read,
     *              false otherwise.
     * @see #isRead()
     * @see #read(String, Object)
     */
    public void setAllReadFlags(boolean state) {
        ReadMe = state;

        for (Enumeration e = varTemplate.elements(); e.hasMoreElements();) {
            ServerMethods sm = (ServerMethods) e.nextElement();
            //System.out.println("Setting Read Flag for "+((BaseType)sm).getName()+" to "+state);
            sm.setRead(state);
        }

    }

    /**
     * Get the value of the Read property.
     *
     * @return true if the variable has been read,
     *         false otherwise.
     * @see #read(String, Object)
     * @see #setRead(boolean)
     */
    public boolean isRead() {
        return (ReadMe);
    }

    /**
     * Read a value from the named dataset for this variable.
     *
     * @param datasetName String identifying the file or other data store
     *                    from which to read a vaue for this variable.
     * @param specialO    This Object is a goody that is used by Server implementations
     *                    to deliver important, and as yet unknown, stuff to the read method. If you
     *                    don't need it, make it a null.
     * @return true if more data remains to be read, otherwise
     *         false. This is an abtsract method that must be implemented
     *         as part of the installation/localization of a OPeNDAP server.
     * @throws IOException
     * @throws EOFException
     */
    public abstract boolean read(String datasetName, Object specialO) throws NoSuchVariableException, IOException, EOFException;

    /**
     * Server-side serialization for OPeNDAP variables (sub-classes of
     * BaseType). This does not send the entire class as the
     * Java Serializable interface does, rather it sends only
     * the binary data values. Other software is responsible for sending
     * variable type information (see DDS).

     *

     * Writes data to a DataOutputStream. This method is used
     * on the server side of the OPeNDAP client/server connection, and possibly
     * 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.
     * @see BaseType
     * @see DDS
     * @see ServerDDS
     */
    public void serialize(String dataset, DataOutputStream sink, CEEvaluator ce, Object specialO)
            throws NoSuchVariableException, DAP2ServerSideException, IOException {

        boolean moreToRead = true;

        while (moreToRead) {

            if (!isRead()) {
// ************* Pulled out the getLevel() check in order to support the "new"
// and "improved" serialization of OPeNDAP sequences. 8/31/01 ndp
//                if(getLevel() != 0 )  // Read only the outermost level
//                    return;
                moreToRead = read(dataset, specialO);
            }

            //System.out.println("Evaluating Clauses...");
            if (ce.evalClauses(specialO)) {
                //System.out.println("Clauses evaluated true");

// ************* Pulled out the getLevel() check in order to support the "new"
// and "improved" serialization of OPeNDAP sequences. 8/31/01 ndp
//                if(getLevel() == 0){
                writeMarker(sink, START_OF_INSTANCE);
//                }

                for (Enumeration e = varTemplate.elements(); e.hasMoreElements();) {
                    ServerMethods sm = (ServerMethods) e.nextElement();
                    if (sm.isProject()) {
                        if (_Debug) System.out.println("Sending variable: " + ((BaseType) sm).getEncodedName());
                        sm.serialize(dataset, sink, ce, specialO);
                    }
                }
            }
            if (moreToRead)
                setAllReadFlags(false);
        }

// ************* Pulled out the getLevel() check in order to support the "new"
// and "improved" serialization of OPeNDAP sequences. 8/31/01 ndp
//        if(getLevel() == 0){
        writeMarker(sink, END_OF_SEQUENCE);
//        }

        return;
    }

    /**
     * Write the variable's declaration in XML. This
     * function is used to create the XML representation of the Data
     * Descriptor Structure (DDS).  See The OPeNDAP User Manual for
     * information about this structure.
     *
     * @param pw          The PrintWriter on which to print the
     *                    declaration.
     * @param pad         Each line of the declaration will begin with the
     *                    characters in this string.  Usually used for leading spaces.
     * @param constrained a boolean value indicating whether to print
     *                    the declartion dependent on the projection information. This
     *                    is only used by Server side code.
     * @see DDS
     */
    public void printXML(PrintWriter pw, String pad, boolean constrained) {

        // BEWARE! Since printXML()is (multiple) overloaded in BaseType
        // and all of the different signatures of printXML() in BaseType
        // lead to one signature, we must be careful to override that
        // SAME signature here. That way all calls to printDecl() for
        // this object lead to this implementation.

        // Also, since printXML()is (multiple) overloaded in BaseType
        // and all of the different signatures of printXML() in BaseType
        // lead to the signature we are overriding here, we MUST call
        // the printXML with the SAME signature THROUGH the super class
        // reference (assuming we want the super class functionality). If
        // we do otherwise, we will create an infinte call loop. OOPS!

        // System.out.println("SDSequence.printXML(pw,pad,"+constrained+") Project:"+Project);

        if (constrained && !isProject())
            return;

        super.printXML(pw, pad, constrained);
    }

}


/////////////////////////////////////////////////////////////////////////////
// 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.*;
import java.util.Enumeration;
import java.util.Vector;

/**
 * A DSequence in OPeNDAP can hold N sequentially accessed
 * instances of a set of variables. In relation to the DStructure
 * datatype, a DSequence is a table
 * of N instances of a DStructure. Data in a
 * DSequence is accessed row by row.
 *

 * Unlike its C++ counterpart, this class reads all of its rows on a
 * deserialize, which gives DSequence the same
 * semantics as the other BaseType classes, eliminating the need
 * to worry about DSequence as a special case.
 *
 * @author jehamby
 * @version $Revision: 22922 $
 * @see BaseType
 * @see DConstructor
 */
public class DSequence extends DConstructor implements ClientIO {
    /**
     * The start of instance byte marker
     */
    static protected byte START_OF_INSTANCE = 0x5A;

    /**
     * The end of sequence byte marker
     */
    static protected byte END_OF_SEQUENCE = (byte) 0xA5;

    /**
     * The variables in this DSequence, stored in a
     * Vector of BaseType objects
     * and used as a template for deserialize.
     */
    protected Vector varTemplate;

    /**
     * The values in this DSequence, stored as a
     * Vector of Vector of BaseType
     * objects.
     */
    protected Vector allValues;

    /**
     * Level number in a multilevel sequence.
     */
    private int level;

    /**
     * Constructs a new DSequence.
     */
    public DSequence() {
        this(null);
    }

    /**
     * Constructs a new DSequence with name n.
     *
     * @param n the name of the variable.
     */
    public DSequence(String n) {
        super(n);
        varTemplate = new Vector();
        allValues = new Vector();
        level = 0;
    }

    /**
     * Returns the OPeNDAP type name of the class instance as a String.
     *
     * @return the OPeNDAP type name of the class instance as a String.
     */
    public String getTypeName() {
        return "Sequence";
    }

    /**
     * Sets the level of this sequence.
     *
     * @param level the new level.
     */
    protected final void setLevel(int level) {
        this.level = level;
    }

    /**
     * Returns the level of this sequence.
     *
     * @return the level of this sequence.
     */
    protected final int getLevel() {
        return level;
    }

    /**
     * Returns the number of variables contained in this object. For simple and
     * vector type variables, it always returns 1. To count the number
     * of simple-type variable in the variable tree rooted at this variable, set
     * leaves to true.
     *
     * @param leaves If true, count all the simple types in the `tree' of
     *               variables rooted at this variable.
     * @return the number of contained variables.
     */
    public int elementCount(boolean leaves) {
        if (!leaves)
            return varTemplate.size();
        else {
            int count = 0;
            for (Enumeration e = varTemplate.elements(); e.hasMoreElements();) {
                BaseType bt = (BaseType) e.nextElement();
                count += bt.elementCount(leaves);
            }
            return count;
        }
    }

    /**
     * Adds a variable to the container.
     *
     * @param v    the variable to add.
     * @param part ignored for DSequence.
     */
    public void addVariable(BaseType v, int part) {
        v.setParent(this);
        varTemplate.addElement(v);
        if (v instanceof DSequence)
            ((DSequence) v).setLevel(getLevel() + 1);
    }

    /**
     * Adds a row to the container.  This is assumed to contain a
     * Vector of variables of the same type and in the same order
     * as the variable template added with the addVariable method.
     *
     * @param row the Vector to add.
     */
    public final void addRow(Vector row) {
        allValues.addElement(row);
    }

    /**
     * Gets a row from the container.  This returns a Vector of
     * variables of the same type and in the same order as the variable template
     * added with the addVariable method.
     *
     * @param row the row number to retrieve.
     * @return the Vector of BaseType variables.
     */
    public final Vector getRow(int row) {
        return (Vector) allValues.elementAt(row);
    }

    /**
     * Deletes a row from the container.
     *
     * @param row the row number to delete.
     * @throws ArrayIndexOutOfBoundsException if the index was invalid.
     */
    public final void delRow(int row) {
        allValues.removeElementAt(row);
    }

    /**
     * Returns the number of rows in this Sequence.
     *
     * @return the number of rows currently in this Sequence.
     */
    public int getRowCount() {
        return allValues.size();
    }

    /**
     * Returns the named variable.  Note: In DSequence,
     * this method returns the template variable, which holds no data.  If you
     * need to get a variable containing data, use getRow or the
     * getVariable method which takes a row number parameter.
     *
     * @param name the name of the variable.
     * @return the named variable.
     * @throws NoSuchVariableException if the named variable does not
     *                                 exist in this container.
     * @see DSequence#getVariable(int, String)
     */
    public BaseType getVariable(String name) throws NoSuchVariableException {

        int dotIndex = name.indexOf('.');

        if (dotIndex != -1) {  // name contains "."
            String aggregate = name.substring(0, dotIndex);
            String field = name.substring(dotIndex + 1);

            BaseType aggRef = getVariable(aggregate);
            if (aggRef instanceof DConstructor)
                return ((DConstructor) aggRef).getVariable(field);  // recurse
            else
                ; // fall through to throw statement
        } else {
            for (Enumeration e = varTemplate.elements(); e.hasMoreElements();) {
                BaseType v = (BaseType) e.nextElement();
                if (v.getEncodedName().equals(name))
                    return v;
            }
        }
        throw new NoSuchVariableException("DSequence: getVariable()");
    }

    /**
     * Gets the indexed variable. For a DSrquence this returns the BaseType
     * from the indexth column from the internal map Vector.
     *
     * @param index the index of the variable in the Vector Vars.
     * @return the indexed variable.
     * @throws NoSuchVariableException if the named variable does not
     *                                 exist in this container.
     */
    public BaseType getVar(int index)
            throws NoSuchVariableException {

        if (index < varTemplate.size())
            return ((BaseType) varTemplate.elementAt(index));
        else
            throw new NoSuchVariableException("DSequence.getVariable(" + index + " - 1)");

    }

    /**
     * Get the number of contained variables (for use with getVar()
     * @return the number of contained variables
     */
    public int getVarCount()
    {
	return varTemplate.size();
    }

    /**
     * Returns the named variable in the given row of the sequence.
     *
     * @param row  the row number to retrieve.
     * @param name the name of the variable.
     * @return the named variable.
     * @throws NoSuchVariableException if the named variable does not
     *                                 exist in this container.
     */
    public BaseType getVariable(int row, String name) throws NoSuchVariableException {

        int dotIndex = name.indexOf('.');

        if (dotIndex != -1) {  // name contains "."
            String aggregate = name.substring(0, dotIndex);
            String field = name.substring(dotIndex + 1);

            BaseType aggRef = getVariable(aggregate);
            if (aggRef instanceof DConstructor)
                return ((DConstructor) aggRef).getVariable(field);  // recurse
            else
                ; // fall through to throw statement
        } else {
            Vector selectedRow = (Vector) allValues.elementAt(row);
            for (Enumeration e = selectedRow.elements(); e.hasMoreElements();) {
                BaseType v = (BaseType) e.nextElement();
                if (v.getEncodedName().equals(name))
                    return v;
            }
        }
        throw new NoSuchVariableException("DSequence: getVariable()");
    }

    /**
     * Return an Enumeration that can be used to iterate over the members of
     * a Sequence. This implementation provides access to the template
     * elements of the Sequence, not the entire sequence. Each Object
     * returned by the Enumeration can be cast to a BaseType.
     *
     * @return An Enumeration
     */
    public Enumeration getVariables() {
        return varTemplate.elements();
    }

    /**
     * Checks for internal consistency.  For DSequence, verify that
     * the variables have unique names.
     *
     * @param all for complex constructor types, this flag indicates whether to
     *            check the semantics of the member variables, too.
     * @throws BadSemanticsException if semantics are bad, explains why.
     * @see BaseType#checkSemantics(boolean)
     */
    public void checkSemantics(boolean all)
            throws BadSemanticsException {
        super.checkSemantics(all);

        Util.uniqueNames(varTemplate, getEncodedName(), getTypeName());

        if (all) {
            for (Enumeration e = varTemplate.elements(); e.hasMoreElements();) {
                BaseType bt = (BaseType) e.nextElement();
                bt.checkSemantics(true);
            }
        }
    }

    /**
     * Write the variable's declaration in a C-style syntax. This
     * function is used to create textual representation of the Data
     * Descriptor Structure (DDS).  See The OPeNDAP User Manual for
     * information about this structure.
     *
     * @param os         The PrintWriter on which to print the
     *                   declaration.
     * @param space      Each line of the declaration will begin with the
     *                   characters in this string.  Usually used for leading spaces.
     * @param print_semi a boolean value indicating whether to print a
     *                   semicolon at the end of the declaration.
     * @see BaseType#printDecl(PrintWriter, String, boolean)
     */
    //Coverity[CALL_SUPER]
    public void printDecl(PrintWriter os, String space,
                          boolean print_semi, boolean constrained) {

        // BEWARE! Since printDecl()is (multiple) overloaded in BaseType
        // and all of the different signatures of printDecl() in BaseType
        // lead to one signature, we must be careful to override that
        // SAME signature here. That way all calls to printDecl() for
        // this object lead to this implementation.

        os.println(space + getTypeName() + " {");
        for (Enumeration e = varTemplate.elements(); e.hasMoreElements();) {
            BaseType bt = (BaseType) e.nextElement();
            //os.println("Printing declaration for \""+bt.getName()+"\"   constrained: "+constrained);
            bt.printDecl(os, space + "    ", true, constrained);
        }
        os.print(space + "} " + getEncodedName());
        if (print_semi)
            os.println(";");
    }

    /**
     * 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.
     * @see BaseType#printVal(PrintWriter, String, boolean)
     */
    public void printVal(PrintWriter os, String space, boolean print_decl_p) {
        if (print_decl_p) {
            printDecl(os, space, false);
            os.print(" = ");
        }

        os.print("{ ");
        for (Enumeration e1 = allValues.elements(); e1.hasMoreElements();) {
            // get next instance vector
            os.print("{ ");
            Vector v = (Vector) e1.nextElement();
            for (Enumeration e2 = v.elements(); e2.hasMoreElements();) {
                // get next instance variable
                BaseType bt = (BaseType) e2.nextElement();
                bt.printVal(os, "", false);
                if (e2.hasMoreElements())
                    os.print(", ");
            }
            os.print(" }");
            if (e1.hasMoreElements())
                os.print(", ");
        }
        os.print(" }");

        if (print_decl_p)
            os.println(";");
    }

    /**
     * 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.
     * @param statusUI the StatusUI object to use for GUI updates
     *                 and user cancellation notification (may be null).
     * @throws EOFException      if EOF is found before the variable is completely
     *                           deserialized.
     * @throws IOException       thrown on any other InputStream exception.
     * @throws DataReadException if an unexpected value was read.
     * @see ClientIO#deserialize(DataInputStream, ServerVersion, StatusUI)
     */
    public synchronized void deserialize(DataInputStream source,
                                         ServerVersion sv,
                                         StatusUI statusUI)
            throws IOException, DataReadException {

        //LogStream.out.println("ServerVersion.getMajor() "+sv.getMajor()+"  ServerVersion.getMinor(): "+sv.getMinor());

        // check for old servers
        if (sv != null && (sv.getMajor() < 2 || (sv.getMajor() == 2 && sv.getMinor() < 15))) {
            //LogStream.out.println("Using oldDeserialize() mechanism.");
            oldDeserialize(source, sv, statusUI);
        } else {
            //LogStream.out.println("Using new deserialize() mechanism.");

// ************* Pulled out the getLevel() check in order to support the "new"
// and "improved" serialization of OPeNDAP sequences. 8/31/01 ndp
//            // top level of sequence handles start and end markers
//            if (getLevel() == 0) {
            // loop until end of sequence
            for (; ;) {

                byte marker = readMarker(source);

                if (statusUI != null)
                    statusUI.incrementByteCount(4);

                if (marker == START_OF_INSTANCE)
                    deserializeSingle(source, sv, statusUI);
                else if (marker == END_OF_SEQUENCE)
                    break;
                else
                    throw new DataReadException("Sequence start marker not found");
            }
// ************* Pulled out the getLevel() check in order to support the "new"
// and "improved" serialization of OPeNDAP sequences. 8/31/01 ndp
//            }
//	    else {
//                // lower levels only deserialize a single instance at a time
//                deserializeSingle(source, sv, statusUI);
//            }
        }
    }

    /**
     * The old deserialize protocol has a number of limitations stemming from
     * its inability to tell when the sequence is finished.  It's really only
     * good for a Dataset containing a single sequence, or where the sequence is
     * the last thing in the dataset.  To handle this, we just read single
     * instances until we get an IOException, then stop.
     *
     * @param source   a DataInputStream to read from.
     * @param sv       the ServerVersion returned by the server.
     * @param statusUI the StatusUI object to use for GUI updates
     *                 and user cancellation notification (may be null).
     * @throws IOException       thrown on any InputStream exception other than EOF
     *                           (which is trapped here).
     * @throws DataReadException if an unexpected value was read.
     */
    private void oldDeserialize(DataInputStream source, ServerVersion sv,
                                StatusUI statusUI)
            throws IOException, DataReadException {
        try {
            for (; ;) {
                deserializeSingle(source, sv, statusUI);
            }
        }
        catch (EOFException e) {
        }
    }

    /**
     * Deserialize a single row of the DSequence.
     *
     * @param source   a DataInputStream to read from.
     * @param sv       the ServerVersion returned by the server.
     * @param statusUI the StatusUI object to use for GUI updates
     *                 and user cancellation notification (may be null).
     * @throws EOFException      if EOF is found before the variable is completely
     *                           deserialized.
     * @throws IOException       thrown on any other InputStream exception.
     * @throws DataReadException if an unexpected value was read.
     */
    private void deserializeSingle(DataInputStream source, ServerVersion sv,
                                   StatusUI statusUI)
            throws IOException, EOFException, DataReadException {
        // create a new instance from the variable template Vector
        Vector newInstance = new Vector();
        for (int i = 0; i < varTemplate.size(); i++) {
            BaseType bt = (BaseType) varTemplate.elementAt(i);
            newInstance.addElement(bt.clone());
        }
        // deserialize the new instance
        for (Enumeration e = newInstance.elements(); e.hasMoreElements();) {
            if (statusUI != null && statusUI.userCancelled())
                throw new DataReadException("User cancelled");
            ClientIO bt = (ClientIO) e.nextElement();
            bt.deserialize(source, sv, statusUI);
        }
        // add the new instance to the allValues vector
        allValues.addElement(newInstance);
    }

    /**
     * Reads a marker byte from the input stream.
     */
    private byte readMarker(DataInputStream source) throws IOException {
        byte marker = source.readByte();
        // pad out to a multiple of four bytes
        byte unused;
        for (int i = 0; i < 3; i++)
            unused = source.readByte();

        return marker;
    }

    /**
     * Writes a marker byte to the output stream.
     */
    protected void writeMarker(DataOutputStream sink, byte marker) throws IOException {
        //for(int i=0; i<4; i++)
        sink.writeByte(marker);
        sink.writeByte((byte) 0);
        sink.writeByte((byte) 0);
        sink.writeByte((byte) 0);
    }

    /**
     * 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 {

        // loop until end of sequence
        for (int i = 0; i < allValues.size(); i++) {

// ************* Pulled out the getLevel() check in order to support the "new"
// and "improved" serialization of OPeNDAP sequences. 8/31/01 ndp
//            if (getLevel() == 0)
            writeMarker(sink, START_OF_INSTANCE);

            Vector rowVec = (Vector) allValues.elementAt(i);

            for (int j = 0; j < rowVec.size(); j++) {
                ClientIO bt = (ClientIO) rowVec.elementAt(j);
                bt.externalize(sink);
            }

        }
// ************* Pulled out the getLevel() check in order to support the "new"
// and "improved" serialization of OPeNDAP sequences. 8/31/01 ndp
//        if (getLevel() == 0)
        writeMarker(sink, END_OF_SEQUENCE);
    }

    /**
     * Returns a clone of this Sequence.
     * See DAPNode.cloneDag()
     *
     * @param map track previously cloned nodes
     * @return a clone of this object.
     */
    public DAPNode cloneDAG(CloneMap map)
        throws CloneNotSupportedException
    {
        DSequence s = (DSequence) super.cloneDAG(map);

        s.varTemplate = new Vector();

        for (int i = 0; i < varTemplate.size(); i++) {
            BaseType bt = (BaseType) varTemplate.elementAt(i);
	        BaseType btclone = (BaseType)cloneDAG(map,bt);
            s.varTemplate.addElement(btclone);
        }

        s.allValues = new Vector();

        for (int i = 0; i < allValues.size(); i++) {
            Vector rowVec = (Vector) allValues.elementAt(i);
            Vector newVec = new Vector();
            for (int j = 0; j < rowVec.size(); j++) {
                BaseType bt = (BaseType) rowVec.elementAt(j);
                newVec.addElement((BaseType)cloneDAG(map,bt));
            }
            s.allValues.addElement(newVec);
        }
        return s;
    }

}


/* Copyright 2012, UCAR/Unidata.
   See the LICENSE file for more information. */

package dap4.core.dmr.parser.bison;

import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;

abstract public class SaxEventHandler extends DefaultHandler
{
    //////////////////////////////////////////////////
    // Constants

    static boolean TRACE = false;

    static Charset UTF8 = Charset.forName("UTF-8");

    static final String LOAD_EXTERNAL_DTD
            = "http://apache.org/xml/features/nonvalidating/load-external-dtd";

    //////////////////////////////////////////////////
    // static types

    //////////////////////////////////////////////////
    // static fields

    //////////////////////////////////////////////////
    // Instance variables

    protected String document = null;

    // Sax parser state
    protected Locator locator = null;
    protected SAXParserFactory spf = null;
    protected SAXParser saxparser = null;
    protected ByteArrayInputStream input = null;

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

    public SaxEventHandler()
    {

    }

    //////////////////////////////////////////////////
    // Abstract method(s)

    // Send the lexeme to the the subclass to process
    abstract public void yyevent(SaxEvent token) throws SAXException;

    //////////////////////////////////////////////////
    // Get/Set
    public Locator
    getLocator()
    {
        return this.locator;
    }
    //////////////////////////////////////////////////
    // Public API

    public boolean parse(String document)
            throws SAXException
    {
        // Trim and strip any leading <?xml...?>
        StringBuilder doc = new StringBuilder(document.trim());
        int index = doc.indexOf("<?xml");
        if(index == 0) {
            index = doc.indexOf("?>");
            if(index < 0)
                throw new SAXException("Document has malformed <?xml...?> prefix");
            doc.delete(0,index+2);
            // remove any leading crlf
            while(doc.length() > 0 && "\r\n".indexOf(doc.charAt(0)) >= 0)
                doc.deleteCharAt(0);
            document = doc.toString();
        }
        this.document = document;
        // Create the sax parser that will drive us with events
        try {
            spf = SAXParserFactory.newInstance();
            spf.setValidating(false);
            spf.setNamespaceAware(true);
            spf.setFeature(LOAD_EXTERNAL_DTD, false);
            saxparser = spf.newSAXParser();
            // Set up for the parse
            input = new ByteArrayInputStream(document.getBytes(UTF8));
            saxparser.parse(input, this); //'this' is link to subclass parser
            return true;
        } catch (Exception e) {
            throw new SAXException(e);
        }
    }

    //////////////////////////////////////////////////
    // DefaultHandler Overrides

    // We feed only a subset of the possible events into
    // the subclass handler. This can be changed by
    // overriding the suppressing eventtype handlers below.

    @Override
    public void setDocumentLocator(Locator locator)
    {
        this.locator = locator;
    }

    @Override
    public void startDocument()
            throws SAXException
    {
        SaxEvent token = new SaxEvent(SaxEventType.STARTDOCUMENT, locator);
        if(TRACE) trace("eventtype.%s: %s%n", token.eventtype.name(), token.toString());
        locatedEvent(token);
    }

    @Override
    public void endDocument()
            throws SAXException
    {
        SaxEvent token = new SaxEvent(SaxEventType.ENDDOCUMENT, locator);
        if(TRACE) trace("eventtype.%s: %s%n", token.eventtype.name(), token.toString());
        locatedEvent(token);
    }

    @Override
    public void startElement(String nsuri, String name, String qualname,
                             Attributes attributes)
            throws SAXException
    {
        SaxEvent token = new SaxEvent(SaxEventType.STARTELEMENT, locator, name, qualname, nsuri);
        if(TRACE) trace("eventtype.%s: %s%n", token.eventtype.name(), token.toString());
        locatedEvent(token);
        // Now pass the attributes as tokens
        int nattr = attributes.getLength();
        for(int i = 0; i < nattr; i++) {
            String aname = attributes.getLocalName(i);
            if("".equals(aname)) aname = attributes.getQName(i);
            String value = attributes.getValue(i);
            token = new SaxEvent(SaxEventType.ATTRIBUTE, locator, aname);
            token.value = value;
            if(TRACE) trace("eventtype.%s: %s%n", token.eventtype.name(), token.toString());
            locatedEvent(token);
        }
    }

    @Override
    public void endElement(String nsuri, String name, String qualname)
            throws SAXException
    {
        SaxEvent token = new SaxEvent(SaxEventType.ENDELEMENT, locator, name, qualname, nsuri);
        if(TRACE) trace("eventtype.%s: %s%n", token.eventtype.name(), token.toString());
        locatedEvent(token);
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException
    {
        SaxEvent token = new SaxEvent(SaxEventType.CHARACTERS, locator);
        token.text = new String(ch, start, length);
        if(TRACE) trace("eventtype.%s: %s%n", token.eventtype.name(), token.toString());
        locatedEvent(token);
    }

    // Following events are suppressed

    @Override
    public void ignorableWhitespace(char[] ch, int start, int length)
            throws SAXException
    {
        // should never see this since not validating
        return;
    }

    @Override
    public void endPrefixMapping(String prefix)
            throws SAXException
    {
        return;
    }

    @Override
    public void notationDecl(String name, String publicId, String systemId)
            throws SAXException
    {
        return;
    }

    @Override
    public void processingInstruction(String target, String data)
            throws SAXException
    {
        return;
    }

    @Override
    public void skippedEntity(String name)
            throws SAXException
    {
        return;
    }

    @Override
    public void startPrefixMapping(String prefix, String uri)
            throws SAXException
    {
        return;
    }

    @Override
    public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName)
            throws SAXException
    {
        return;
    }

    //////////////////////////////////////////////////
    // Entity resolution (Ignored)

    @Override
    public InputSource resolveEntity(String publicId, String systemId)
    {
        if(TRACE) trace("eventtype.RESOLVEENTITY: %s.%s%n", publicId, systemId);
        return null;
    }

    //////////////////////////////////////////////////
    // Error handling Events

    @Override
    public void fatalError(SAXParseException e)
            throws SAXException
    {
        throw new SAXParseException(
                String.format("Sax fatal error: %s; %s%n", e, report(this.locator)),
                this.locator);
    }

    @Override
    public void error(SAXParseException e)
            throws SAXException
    {
        System.err.printf("Sax error: %s; %s%n", e, report(this.locator));
    }

    @Override
    public void warning(SAXParseException e)
            throws SAXException
    {
        System.err.printf("Sax warning: %s; %s%n", e, report(this.locator));
    }

    protected String
    report(Locator locator)
    {
        int lineno = locator.getLineNumber();
        int colno = locator.getColumnNumber();
        String text = this.document;
        String[] lines = text.split("[\n]");
        for(int i = lines.length; i <= lineno+1; i++) {
            //Coverity[FB.SBSC_USE_STRINGBUFFER_CONCATENATION]
            text = text + " \n";
        }
        lines = text.split("[\n]");
        String msg;
        try {
            msg = lines[lineno];
            while(msg.length() <= colno) {
                msg = msg + ' ';
            }
            msg = msg.substring(0, colno) + '^' + msg.substring(colno, msg.length());
            msg = locator.toString() + '|' + msg + '|';
        } catch (ArrayIndexOutOfBoundsException t) {
            msg = locator.toString();
        }
        return msg;
    }

    //////////////////////////////////////////////////
    // Location printing

    protected void
    locatedEvent(SaxEvent token)
            throws SAXException
    {
        try {
            yyevent(token);
        } catch (SAXException se) {
            throw new SAXException(locatedError(se.getMessage()));
        }
    }

    protected String
    locatedError(String msg)
    {
        String locmsg = msg + String.format("; near %d::%d%n",
                this.locator.getLineNumber(),this.locator.getColumnNumber());
        return locmsg;
    }

    protected void
    trace(String msg, Object... args)
    {
        if(TRACE) System.err.printf(locatedError(String.format(msg, args)));
    }

} // class SaxEventHandler


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

package thredds.ui.monitor;

import java.awt.*;

import javax.swing.*;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.time.Minute;
import org.jfree.data.time.RegularTimePeriod;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;

/**
 * This demo shows a time series chart that has multiple range axes.
 */
public class MultipleAxisChart extends JPanel {

  JFreeChart chart;

  /**
   * A demonstration application showing how to create a time series chart
   * with multiple axes.
   *
   * @param title1 the frame title.
   * @param xaxis1 xaxis title
   * @param yaxis1 yaxis title
   * @param series1 the data
   */
  public MultipleAxisChart(String title1, String xaxis1, String yaxis1, TimeSeries series1) {

    TimeSeriesCollection dataset1 = new TimeSeriesCollection();
    dataset1.addSeries(series1);

    chart = ChartFactory.createTimeSeriesChart(title1, xaxis1, yaxis1, dataset1,
            true,
            true,
            false
    );

    // chart.addSubtitle(new TextTitle("Four datasets and four range axes."));
    XYPlot plot = (XYPlot) chart.getPlot();
    plot.setOrientation(PlotOrientation.VERTICAL);
    plot.getRangeAxis().setFixedDimension(15.0);

    this.setLayout(new BorderLayout());
    this.add(new ChartPanel(chart), BorderLayout.CENTER);
  }

  private int axisNum = 1;
  public void addSeries(String yaxisName, TimeSeries series) {
    NumberAxis axis2 = new NumberAxis(yaxisName);
    axis2.setFixedDimension(10.0);
    axis2.setAutoRangeIncludesZero(false);

    XYPlot plot = (XYPlot) chart.getPlot();
    plot.setRangeAxis(axisNum, axis2);
    plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_LEFT);

    TimeSeriesCollection dataset = new TimeSeriesCollection();
    dataset.addSeries(series);

    plot.setDataset(axisNum, dataset);
    plot.mapDatasetToRangeAxis(axisNum, axisNum);
    XYItemRenderer renderer2 = new StandardXYItemRenderer();
    plot.setRenderer(axisNum, renderer2);

    axisNum++;
  }

  private static final Color[] colors = new Color[] { Color.black, Color.red, Color.blue, Color.green};

  public void finish(java.awt.Dimension preferredSize) {
    ChartUtilities.applyCurrentTheme(chart);

    XYPlot plot = (XYPlot) chart.getPlot();
    for (int i=0; i<axisNum; i++) {
      XYItemRenderer renderer = plot.getRenderer(i);
      if (renderer == null) continue;

      renderer.setSeriesPaint(0, colors[i]);

      ValueAxis axis = plot.getRangeAxis(i);
      axis.setLabelPaint(colors[i]);
      axis.setTickLabelPaint(colors[i]);
    }

    ChartPanel chartPanel = new ChartPanel(chart);
    chartPanel.setPreferredSize(preferredSize);
    chartPanel.setDomainZoomable(true);
    chartPanel.setRangeZoomable(true);
  }

  /**
   * Creates the demo chart.
   *
   * @return The chart.
   *
  private JFreeChart createChart() {

    XYDataset dataset1 = createDataset("Series 1", 100.0, new Minute(), 200);

    JFreeChart chart = ChartFactory.createTimeSeriesChart(
            "Multiple Axis Demo 1",
            "Time of Day",
            "Primary Range Axis",
            dataset1,
            true,
            true,
            false
    );

    chart.addSubtitle(new TextTitle("Four datasets and four range axes."));
    XYPlot plot = (XYPlot) chart.getPlot();
    plot.setOrientation(PlotOrientation.VERTICAL);

    plot.getRangeAxis().setFixedDimension(15.0);

    // AXIS 2
    NumberAxis axis2 = new NumberAxis("Range Axis 2");
    axis2.setFixedDimension(10.0);
    axis2.setAutoRangeIncludesZero(false);
    plot.setRangeAxis(1, axis2);
    plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_LEFT);

    XYDataset dataset2 = createDataset("Series 2", 1000.0, new Minute(),
            170);
    plot.setDataset(1, dataset2);
    plot.mapDatasetToRangeAxis(1, 1);
    XYItemRenderer renderer2 = new StandardXYItemRenderer();
    plot.setRenderer(1, renderer2);

    // AXIS 3
    NumberAxis axis3 = new NumberAxis("Range Axis 3");
    plot.setRangeAxis(2, axis3);

    XYDataset dataset3 = createDataset("Series 3", 10000.0, new Minute(),
            170);
    plot.setDataset(2, dataset3);
    plot.mapDatasetToRangeAxis(2, 2);
    XYItemRenderer renderer3 = new StandardXYItemRenderer();
    plot.setRenderer(2, renderer3);

    // AXIS 4
    NumberAxis axis4 = new NumberAxis("Range Axis 4");
    plot.setRangeAxis(3, axis4);

    XYDataset dataset4 = createDataset("Series 4", 25.0, new Minute(), 200);
    plot.setDataset(3, dataset4);
    plot.mapDatasetToRangeAxis(3, 3);

    XYItemRenderer renderer4 = new StandardXYItemRenderer();
    plot.setRenderer(3, renderer4);

    ChartUtilities.applyCurrentTheme(chart);

    // change the series and axis colours after the theme has
    // been applied...
    plot.getRenderer().setSeriesPaint(0, Color.black);
    renderer2.setSeriesPaint(0, Color.red);
    axis2.setLabelPaint(Color.red);
    axis2.setTickLabelPaint(Color.red);
    renderer3.setSeriesPaint(0, Color.blue);
    axis3.setLabelPaint(Color.blue);
    axis3.setTickLabelPaint(Color.blue);
    renderer4.setSeriesPaint(0, Color.green);
    axis4.setLabelPaint(Color.green);
    axis4.setTickLabelPaint(Color.green);

    return chart;
  }

  /**
   * Creates a sample dataset.
   *
   * @param name  the dataset name.
   * @param base  the starting value.
   * @param start the starting period.
   * @param count the number of values to generate.
   * @return The dataset.
   */
  private static TimeSeries createDataset(String name, double base,
          RegularTimePeriod start, int count) {

    TimeSeries series = new TimeSeries(name, start.getClass());
    RegularTimePeriod period = start;
    double value = base;
    for (int i = 0; i < count; i++) {
      series.add(period, value);
      period = period.next();
      value = value * (1 + (Math.random() - 0.495) / 10.0);
    }
    return series;

  }

  /**
   * Starting point for the demonstration application.
   *
   * @param args ignored.
   */
  public static void main(String[] args) {

    TimeSeries dataset1 = createDataset("Series 1", 100.0, new Minute(), 200);

    MultipleAxisChart demo = new MultipleAxisChart(
            "Multiple Axis Demo 1",
            "Time of Day",
            "Primary Range Axis",
            dataset1);

    /* AXIS 2
    NumberAxis axis2 = new NumberAxis("Range Axis 2");
    axis2.setFixedDimension(10.0);
    axis2.setAutoRangeIncludesZero(false);
    plot.setRangeAxis(1, axis2);
    plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_LEFT); /

    plot.setDataset(1, dataset2);
    plot.mapDatasetToRangeAxis(1, 1);
    XYItemRenderer renderer2 = new StandardXYItemRenderer();
    plot.setRenderer(1, renderer2); */

    TimeSeries dataset2 = createDataset("Series 2", 1000.0, new Minute(), 170);
    demo.addSeries("Range Axis 2", dataset2);

    /*     // AXIS 3
    NumberAxis axis3 = new NumberAxis("Range Axis 3");
    plot.setRangeAxis(2, axis3);

    XYDataset dataset3 = createDataset("Series 3", 10000.0, new Minute(), 170);
    plot.setDataset(2, dataset3);
    plot.mapDatasetToRangeAxis(2, 2);
    XYItemRenderer renderer3 = new StandardXYItemRenderer();
    plot.setRenderer(2, renderer3);
    */
    TimeSeries dataset3 = createDataset("Series 3", 10000.0, new Minute(), 170);
    demo.addSeries("Range Axis 3", dataset3);

    /* AXIS 4
    NumberAxis axis4 = new NumberAxis("Range Axis 4");
    plot.setRangeAxis(3, axis4);

    XYDataset dataset4 = createDataset("Series 4", 25.0, new Minute(), 200);
    plot.setDataset(3, dataset4);
    plot.mapDatasetToRangeAxis(3, 3);

    XYItemRenderer renderer4 = new StandardXYItemRenderer();
    plot.setRenderer(3, renderer4); */
    TimeSeries dataset4 = createDataset("Series 4", 25.0, new Minute(), 200);
    demo.addSeries("Range Axis 4", dataset4);

    demo.finish(new java.awt.Dimension(600, 270));

    JFrame frame = new JFrame("Demovabulous ");
    frame.getContentPane().add(demo, BorderLayout.CENTER);
    frame.setSize(640, 480);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }

}


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

import ucar.nc2.util.Indent;

import java.util.Formatter;

/**
 * A StructureData implementation delegates everything to the containing ArrayStructure.
 *
 * @author caron
 */

public class StructureDataA extends StructureData {
  protected ArrayStructure sa;
  protected int recno;

  /**
   * Constructor.
   *
   * @param sa    StructureData is always contained in a StructureArray.
   * @param recno the recno in the StructureArray.
   */
  public StructureDataA(ArrayStructure sa, int recno) {
    super(sa.getStructureMembers());
    this.sa = sa;
    this.recno = recno;
  }

  public Array getArray(StructureMembers.Member m) {
    return sa.getArray(recno, m);
  }

  public int convertScalarInt(StructureMembers.Member m) {
    return sa.convertScalarInt(recno, m);
  }

  public long convertScalarLong(StructureMembers.Member m) {
    return sa.convertScalarLong(recno, m);
  }

  public float convertScalarFloat(StructureMembers.Member m) {
    return sa.convertScalarFloat(recno, m);
  }

  public double convertScalarDouble(StructureMembers.Member m) {
    return sa.convertScalarDouble(recno, m);
  }

  public double getScalarDouble(StructureMembers.Member m) {
    return sa.getScalarDouble(recno, m);
  }

  public double[] getJavaArrayDouble(StructureMembers.Member m) {
    return sa.getJavaArrayDouble(recno, m);
  }

  /**
   * Get member data of type float.
   *
   * @param m get data from this StructureMembers.Member. Must be of type float.
   * @return scalar float value
   */
  public float getScalarFloat(StructureMembers.Member m) {
    return sa.getScalarFloat(recno, m);
  }

  /**
   * Get member data of type float array.
   *
   * @param m get data from this StructureMembers.Member. Must be of type float.
   * @return 1D array of floats
   */
  public float[] getJavaArrayFloat(StructureMembers.Member m) {
    return sa.getJavaArrayFloat(recno, m);
  }

  /**
   * Get member data of type byte.
   *
   * @param m get data from this StructureMembers.Member. Must be of type byte.
   * @return scalar byte value
   */
  public byte getScalarByte(StructureMembers.Member m) {
    return sa.getScalarByte(recno, m);
  }

  /**
   * Get member data of type byte array.
   *
   * @param m get data from this StructureMembers.Member. Must be of type byte.
   * @return 1D array of bytes
   */
  public byte[] getJavaArrayByte(StructureMembers.Member m) {
    return sa.getJavaArrayByte(recno, m);
  }

  /**
   * Get member data of type short.
   *
   * @param m get data from this StructureMembers.Member. Must be of type short.
   * @return scalar short value
   */
  public short getScalarShort(StructureMembers.Member m) {
    return sa.getScalarShort(recno, m);
  }

  /**
   * Get member data of type short array.
   *
   * @param m get data from this StructureMembers.Member. Must be of type short.
   * @return 1D array of shorts
   */
  public short[] getJavaArrayShort(StructureMembers.Member m) {
    return sa.getJavaArrayShort(recno, m);
  }

  /**
   * Get member data of type int.
   *
   * @param m get data from this StructureMembers.Member. Must be of type int.
   * @return scalar int value
   */
  public int getScalarInt(StructureMembers.Member m) {
    return sa.getScalarInt(recno, m);
  }

  /**
   * Get member data of type int array.
   *
   * @param m get data from this StructureMembers.Member. Must be of type int.
   * @return 1D array of ints
   */
  public int[] getJavaArrayInt(StructureMembers.Member m) {
    return sa.getJavaArrayInt(recno, m);
  }

  /**
   * Get member data of type long.
   *
   * @param m get data from this StructureMembers.Member. Must be of type long.
   * @return scalar long value
   */
  public long getScalarLong(StructureMembers.Member m) {
    return sa.getScalarLong(recno, m);
  }

  /**
   * Get member data of type long array.
   *
   * @param m get data from this StructureMembers.Member. Must be of type long.
   * @return 1D array of longs
   */
  public long[] getJavaArrayLong(StructureMembers.Member m) {
    return sa.getJavaArrayLong(recno, m);
  }

  /**
   * Get member data of type char.
   *
   * @param m get data from this StructureMembers.Member. Must be of type char.
   * @return scalar char value
   */
  public char getScalarChar(StructureMembers.Member m) {
    return sa.getScalarChar(recno, m);
  }

  /**
   * Get member data of type char array.
   *
   * @param m get data from this StructureMembers.Member. Must be of type char.
   * @return 1D array of chars
   */
  public char[] getJavaArrayChar(StructureMembers.Member m) {
    return sa.getJavaArrayChar(recno, m);
  }

  /**
   * Get member data of type String or 1D char.
   *
   * @param m get data from this StructureMembers.Member. Must be of type char.
   * @return scalar char value
   */
  public String getScalarString(StructureMembers.Member m) {
    return sa.getScalarString(recno, m);
  }

  /*
   * Get String value, from rank 0 String or rank 1 char member array.
   * For more efficiency, use getScalarString(StructureMembers.Member m) if possible.
   *
   * @param memberName name of member Variable.
   * @throws IllegalArgumentException if name is not legal member name.
   *
  public String getScalarString(String memberName) {
    StructureMembers.Member m = findMember(memberName);
    if (null == m) throw new IllegalArgumentException("Member not found= " + memberName);
    if ((m.getDataType() == DataType.CHAR) || (m.getDataType() == DataType.STRING))
      return sa.getScalarString(recno, m);

    Array a = getArray(m);
    if (a == null) throw new IllegalArgumentException("illegal member name =" + memberName);
    if (a instanceof ArrayChar.D1)
      return ((ArrayChar.D1) a).getString();

    assert (a instanceof ArrayObject.D0);
    Object data = ((ArrayObject.D0) a).get();
    assert (data instanceof String) : data.getClass().getName();
    return (String) data;
  }  */

  // LOOK can we optimize ??
  public String[] getJavaArrayString(StructureMembers.Member m) {
    if (m.getDataType() == DataType.STRING) {
      Array data = getArray(m);
      int n = m.getSize();
      String[] result = new String[n];
      for (int i = 0; i < result.length; i++)
        result[i] = (String) data.getObject(i);
      return result;

    } else if (m.getDataType() == DataType.CHAR) {
      ArrayChar data = (ArrayChar) getArray(m);
      ArrayChar.StringIterator iter = data.getStringIterator();
      String[] result = new String[iter.getNumElems()];
      int count = 0;
      while (iter.hasNext())
        result[count++] = iter.next();
      return result;
    }

    throw new IllegalArgumentException("getJavaArrayString: not String DataType :" + m.getDataType());
  }

  public StructureData getScalarStructure(StructureMembers.Member m) {
    return sa.getScalarStructure(recno, m);
  }

  public ArrayStructure getArrayStructure(StructureMembers.Member m) {
    return sa.getArrayStructure(recno, m);
  }

  public ArraySequence getArraySequence(StructureMembers.Member m) {
    return sa.getArraySequence(recno, m);
  }

  @Override
  public void showInternal(Formatter f, Indent indent) {
    super.showInternal(f, indent);
    sa.showInternal(f, indent);
  }

}


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

package ucar.atd.dorade;

import ucar.nc2.constants.CDM;

import java.io.RandomAccessFile;
import java.util.Calendar;
import java.util.Date;

/* $Id:DoradeVOLD.java 51 2006-07-12 17:13:13Z caron $ */

class DoradeVOLD extends DoradeDescriptor {

    private short formatRev;
    private short volNumber;
    private int maxRecLen;
    private String projectName;
    private Date dataTime;
    private String flightId;
    private String facilityName;
    private Date fileGenTime;
    private short nSensors;
    private DoradeRADD[] myRADDs;

    public DoradeVOLD(RandomAccessFile file, boolean littleEndianData)
            throws DescriptorException {
        byte[] data = readDescriptor(file, littleEndianData, "VOLD");

        //
        // unpack
        //
        formatRev = grabShort(data, 8);
        volNumber = grabShort(data, 10);
        maxRecLen = grabInt(data, 12);

        projectName = new String(data, 16, 20, CDM.utf8Charset).trim();

        Calendar calendar = Calendar.getInstance(TZ_UTC);

        short year = grabShort(data, 36);
        short month = grabShort(data, 38);
        short day = grabShort(data, 40);
        short hour = grabShort(data, 42);
        short minute = grabShort(data, 44);
        short second = grabShort(data, 46);
        calendar.clear();
        calendar.set(year, month - 1, day, hour, minute, second);
        dataTime = calendar.getTime();

        flightId = new String(data, 48, 8, CDM.utf8Charset).trim();
        facilityName = new String(data, 56, 8, CDM.utf8Charset).trim();

        year = grabShort(data, 64);
        month = grabShort(data, 66);
        day = grabShort(data, 68);
        calendar.clear();
        calendar.set(year, month - 1, day);
        fileGenTime = calendar.getTime();

        nSensors = grabShort(data, 70);

        //
        // debugging output
        //
        if (verbose)
            System.out.println(this);

        //
        // get our myRADDs
        //
        myRADDs = new DoradeRADD[nSensors];
        for (int i = 0; i < nSensors; i++)
            myRADDs[i] = new DoradeRADD(file, littleEndianData);

    }

    public String toString() {
        String s = "VOLD\n";
        s += "  format: " + formatRev + "\n";
        s += "  volume: " + volNumber + "\n";
        s += "  maxRecLen: " + maxRecLen + "\n";
        s += "  project: " + projectName + "\n";
        s += "  data time: " + formatDate(dataTime) + "\n";
        s += "  flight id: " + flightId + "\n";
        s += "  facility name: " + facilityName + "\n";
        s += "  file made: " + formatDate(fileGenTime) + "\n";
        s += "  num sensors: " + nSensors;
        return s;
    }

    /**
     * Get the number of sensors (radars) associated with this VOLD.
     * @return the number of sensors
     */
    public int getNSensors() {
        return nSensors;
    }

    /**
     * Get the name of the nth sensor (radar) associated with this VOLD.
     * @param n  the index of the desired sensor
     * @return the name of the nth sensor
     * @see #getNSensors()
     */
    public String getSensorName(int n) {
        return myRADDs[n].getRadarName();
    }

    /**
     * Get the DoradeRADD for the nth sensor (radar).
     * @param n  the index of the desired sensor
     * @return the DoradeRADD for the nth sensor
     * @see #getNSensors()
     */
    public DoradeRADD getRADD(int n) {
        return myRADDs[n];
    }

    /**
     * Get the time for this volume.  In practice, this means the volume start
     * time, but this is not stated as part of the definition of DORADE.
     * @return the time for this volume
     */
    public Date getVolumeTime() {
        return dataTime;
    }

    // unidata added
    public short getVolumeNumber() {
        return volNumber;
    }

    // unidata added
    public String getProjectName() {
        return projectName;
    }

    /**
     * Get the array of available parameter names for this volume.
     * @return an array of parameter names
     */
    public DoradePARM[] getParamList() {
        int paramCount = 0;
        for (int i = 0; i < nSensors; i++)
            paramCount += myRADDs[i].getNParams();

        DoradePARM[] list = new DoradePARM[paramCount];
        int next = 0;
        for (int i = 0; i < nSensors; i++) {
            int nParams = myRADDs[i].getNParams();
            System.arraycopy(myRADDs[i].getParamList(), 0, list, next, nParams);
            next += nParams;
        }
        return list;
    }

}