/////////////////////////////////////////////////////////////////////////////
// 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.util.Vector;
import java.util.Enumeration;
import java.io.*;
import opendap.dap.*;
import opendap.dap.parsers.DDSXMLParser;
/**
* Holds a OPeNDAP Server <code>Grid</code> value.
*
* @author ndp
* @version $Revision: 19676 $
* @see BaseType
*/
public abstract class SDGrid extends DGrid implements ServerArrayMethods, RelOps {
private boolean Synthesized;
private boolean ReadMe;
Vector AP;
/**
* Constructs a new <code>SDGrid</code>.
*/
public SDGrid() {
super();
Synthesized = false;
ReadMe = false;
}
/**
* Constructs a new <code>SDGrid</code> with name <code>n</code>.
*
* @param n the name of the variable.
*/
public SDGrid(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 <em>The OPeNDAP User Manual</em> for
* information about this structure.
*
* @param os The <code>PrintWriter</code> 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)
*/
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.
boolean isSingle = false;
boolean isStructure = false;
boolean isGrid = false;
boolean psemi = true;
//os.println("The grid contains "+projectedComponents(true)+" projected components");
if (constrained && projectedComponents(true) == 0)
return;
// If we are printing the declaration of a constrained Grid then check for
// the case where the projection removes all but one component; the
// resulting object is a simple array.
// 2013-2-26: Heimbigner : this is incorrect, even single
// projected components should be in a structure.
/* Wrong
if (constrained && projectedComponents(true) == 1) {
//os.println("It's a single Array.");
isSingle = true;
psemi = print_semi;
} */
// If there are M (< N) components (Array and Maps combined) in a N
// component Grid, send the M components as elements of a Structure.
// This will preserve the grouping without violating the rules for a
// Grid.
else if (constrained && !projectionYieldsGrid(true)) {
//os.println("It's a Structure.");
isStructure = true;
} else {
// The number of elements in the (projected) Grid must be such that
// we have a valid Grid object; send it as such.
//os.println("It's a Grid.");
isGrid = true;
}
if (isGrid) os.println(space + getTypeName() + " {");
if (isGrid) os.println(space + " ARRAY:");
if (isStructure) os.println(space + "Structure {");
((SDArray) arrayVar).printDecl(os, space + " ", psemi, constrained);
if (isGrid) os.println(space + " MAPS:");
for (Enumeration e = mapVars.elements(); e.hasMoreElements();) {
SDArray sda = (SDArray) e.nextElement();
sda.printDecl(os, space + " ", psemi, constrained);
}
if (isStructure || isGrid) {
os.print(space + "} " + getEncodedName());
if (print_semi)
os.println(";");
}
return;
}
/**
* 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.
* <p/>
* <h2> Important Note</h2>
* This method overrides the BaseType method of the same name and
* type signature and it significantly changes the behavior for all versions
* of <code>printVal()</code> for this type:
* <b><i> All the various versions of printVal() will only
* print a value, or a value with declaration, if the variable is
* in the projection.</i></b>
* <br>
* <br>In other words, if a call to
* <code>isProject()</code> for a particular variable returns
* <code>true</code> then <code>printVal()</code> will print a value
* (or a declaration and a value).
* <br>
* <br>If <code>isProject()</code> for a particular variable returns
* <code>false</code> then <code>printVal()</code> is basically a No-Op.
* <br>
* <br>
*
* @param os the <code>PrintWriter</code> on which to print the value.
* @param space this value is passed to the <code>printDecl</code> 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;
//System.out.println("\nSome Part of this object is projected...");
if (print_decl_p) {
printDecl(os, space, false, true);
os.print(" = ");
}
boolean isStillGrid = projectionYieldsGrid(true);
os.print("{ ");
if (isStillGrid) os.print("ARRAY: ");
if (((SDArray) arrayVar).isProject())
arrayVar.printVal(os, "", false);
if (isStillGrid) os.print(" MAPS: ");
boolean firstPass = true;
Enumeration e = mapVars.elements();
while (e.hasMoreElements()) {
SDArray sda = (SDArray) e.nextElement();
if (((SDArray) sda).isProject()) {
if (!firstPass) os.print(", ");
sda.printVal(os, "", false);
firstPass = false;
}
}
os.print(" }");
if (print_decl_p)
os.println(";");
}
/**
* Adds a variable to the container. This overrides the same method in
* the parent class <code>DGrid</code> in order to add array projection
* functionality.
*
* @param v the variable to add.
* @param part the part of the <code>DGrid</code> to be modified. Allowed
* values are <code>ARRAY</code> or <code>MAPS</code>.
* @throws IllegalArgumentException if an invalid part was given.
*/
public void addVariable(BaseType v, int part) {
super.addVariable(v, part);
}
// --------------- Projection Interface
/**
* Set the state of this variable's projection. <code>true</code> 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 <code>false</code>.
*
* @param state <code>true</code> if the variable is part of the current
* projection, <code>false</code> otherwise.
* @param all If <code>true</code>, 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) {
// System.out.println("SDGrid:setProject: Blindly setting Project");
((SDArray) arrayVar).setProject(state);
for (Enumeration e = mapVars.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. DGrid 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 SDGrid!");
}
public boolean not_equal(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
throw new InvalidOperatorException("Not Equals (!=) operator does not work with the type SDGrid!");
}
public boolean greater(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
throw new InvalidOperatorException("Greater Than (>)operator does not work with the type SDGrid!");
}
public boolean greater_eql(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
throw new InvalidOperatorException("GreaterThan or equals (<=) operator does not work with the type SDGrid!");
}
public boolean less(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
throw new InvalidOperatorException("LessThan (<) operator does not work with the type SDGrid!");
}
public boolean less_eql(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
throw new InvalidOperatorException("LessThan oe equals (<=) operator does not work with the type SDGrid!");
}
public boolean regexp(BaseType bt) throws InvalidOperatorException, RegExpException, SBHException {
throw new InvalidOperatorException("Regular Expression's don't work with the type SDGrid!");
}
// --------------- FileIO Interface
/**
* Set the Synthesized property.
*
* @param state If <code>true</code> then the variable is considered a
* synthetic variable and no part of OPeNDAP will ever try to read it from a
* file, otherwise if <code>false</code> the variable is considered a
* normal variable whose value should be read using the
* <code>read()</code> 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 <code>true</code> if this is a synthetic variable,
* <code>false</code> otherwise.
*/
public boolean isSynthesized() {
return (Synthesized);
}
/**
* Set the Read property. A normal variable is read using the
* <code>read()</code> method. Once read the <em>Read</em> property is
* <code>true</code>. Use this function to manually set the property
* value. By default this property is false.
*
* @param state <code>true</code> if the variable has been read,
* <code>false</code> otherwise.
* @see #isRead()
* @see #read(String, Object)
*/
public void setRead(boolean state) {
ReadMe = state;
}
/**
* Get the value of the Read property.
*
* @return <code>true</code> if the variable has been read,
* <code>false</code> 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 <code>Object</code> 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 <code>null</code>.
* @return <code>true</code> if more data remains to be read, otherwise
* <code>false</code>. 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
* <code>BaseType</code>).
* This does not send the entire class as the Java <code>Serializable</code>
* interface does, rather it sends only the binary data values. Other software
* is responsible for sending variable type information (see <code>DDS</code>).
* <p/>
* Writes data to a <code>DataOutputStream</code>. 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 <code>DataOutputStream</code> to write to.
* @throws IOException thrown on any <code>OutputStream</code> exception.
* @see BaseType
* @see DDS
* @see ServerDDS
*/
public void serialize(String dataset, DataOutputStream sink, CEEvaluator ce, Object specialO)
throws NoSuchVariableException, DAP2ServerSideException, IOException {
if (!isRead())
read(dataset, specialO);
if (ce.evalClauses(specialO)) {
if (((ServerMethods) arrayVar).isProject())
((ServerMethods) arrayVar).serialize(dataset, sink, ce, specialO);
for (Enumeration e = mapVars.elements(); e.hasMoreElements();) {
ServerMethods sm = (ServerMethods) e.nextElement();
if (sm.isProject())
sm.serialize(dataset, sink, ce, specialO);
}
}
}
//---------------------------------------------------------------------------------
//------------------------- Array Projection Interface ----------------------------
//..................................................................................
/**
* Set the projection information for this dimension. The internal <code>DArray</code> is
* retrieved and then the <code>DArrayDimension</code>
* associated with the <code>dimension</code> specified is retrieved and the <code>start</code>
* <code>stride</code> and <code>stop</code> parameters are passed to its
* <code>setProjection()</code> method.
*
* @param dimension The dimension that is to be modified.
* @param start The starting point for the projection of this <code>DArrayDimension</code>.
* @param stride The size of the stride for the projection of this <code>DArrayDimension</code>.
* @param stop The stopping point for the projection of this <code>DArrayDimension</code>.
* @see DArray
* @see DArrayDimension
*/
public void setProjection(int dimension, int start, int stride, int stop)
throws InvalidDimensionException, SBHException {
try {
DArray a = (DArray) getVar(0);
DArrayDimension d = a.getDimension(dimension);
d.setProjection(start, stride, stop);
DArray map = (DArray) getVar(dimension + 1);
DArrayDimension mapD = map.getDimension(0);
mapD.setProjection(start, stride, stop);
}
catch (NoSuchVariableException e) {
throw new InvalidDimensionException("SDGrid.setProjection(): Bad Value for dimension!: "
+ e.getMessage());
}
}
/**
* Gets the <b>start</b> value for the projection of the
* <code>dimension</code> indicated. The parameter
* <code>dimension</code> is checked against the instance of the
* <code>SDArray</code> for bounds violation.
*
* @param dimension The dimension from whose projection to retrieve the
* <code>start</code> value.
*/
public int getStart(int dimension) throws InvalidDimensionException {
try {
DArray a = (DArray) getVar(0);
DArrayDimension d = a.getDimension(dimension);
return (d.getStart());
}
catch (NoSuchVariableException e) {
throw new InvalidDimensionException("SDGrid.getStart(): Bad Value for dimension!: "
+ e.getMessage());
}
}
/**
* Gets the <b>stride</b> value for the projection of the
* <code>dimension</code> indicated. The parameter
* <code>dimension</code> is checked against the instance of the
* <code>SDArray</code> for bounds violation.
*
* @param dimension The dimension from whose projection to retrieve the
* <code>stride</code> value.
*/
public int getStride(int dimension) throws InvalidDimensionException {
try {
DArray a = (DArray) getVar(0);
DArrayDimension d = a.getDimension(dimension);
return (d.getStride());
}
catch (NoSuchVariableException e) {
throw new InvalidDimensionException("SDGrid.getStride(): Bad Value for dimension!: "
+ e.getMessage());
}
}
/**
* Gets the <b>stop</b> value for the projection of the
* <code>dimension</code> indicated. The parameter
* <code>dimension</code> is checked against the instance of the
* <code>SDArray</code> for bounds violation.
*
* @param dimension The dimension from whose projection to retrieve the
* <code>stop</code> value.
*/
public int getStop(int dimension) throws InvalidDimensionException {
try {
DArray a = (DArray) getVar(0);
DArrayDimension d = a.getDimension(dimension);
return (d.getStop());
}
catch (NoSuchVariableException e) {
throw new InvalidDimensionException("SDGrid.getStop(): Bad Value for dimension!: "
+ e.getMessage());
}
}
/**
* 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 <em>The OPeNDAP User Manual</em> for
* information about this structure.
*
* @see BaseType#printDecl(PrintWriter, String, boolean)
* @opendap.ddx.experimental
*/
public void printXML(PrintWriter pw, String pad, 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.
boolean isSingle = false;
boolean isStructure = false;
boolean isGrid = false;
boolean psemi = true;
//os.println("The gird contains "+projectedComponents(true)+" projected components");
if (constrained && projectedComponents(true) == 0)
return;
// If we are printing the declaration of a constrained Grid then check for
// the case where the projection removes all but one component; the
// resulting object is a simple array.
if (constrained && projectedComponents(true) == 1) {
//os.println("It's a single Array.");
isSingle = true;
}
// If there are M (< N) componets (Array and Maps combined) in a N
// component Grid, send the M components as elements of a Struture.
// This will preserve the grouping without violating the rules for a
// Grid.
else if (constrained && !projectionYieldsGrid(true)) {
//os.println("It's a Structure.");
isStructure = true;
} else {
// The number of elements in the (projected) Grid must be such that
// we have a valid Grid object; send it as such.
//os.println("It's a Grid.");
isGrid = true;
}
if (isGrid) {
pw.print(pad + "<Grid ");
if (getEncodedName() != null) {
pw.print(" name=\"" +
DDSXMLParser.normalizeToXML(getEncodedName()) + "\"");
}
pw.println(">");
}
if (isStructure) {
pw.print(pad + "<Structure");
if (getEncodedName() != null) {
pw.print(" name=\"" +
DDSXMLParser.normalizeToXML(getEncodedName()) + "\"");
}
pw.println(">");
}
Enumeration e = getAttributeNames();
while (e.hasMoreElements()) {
String aName = (String) e.nextElement();
Attribute a = getAttribute(aName);
if(a!=null)
a.printXML(pw, pad + "\t", constrained);
}
((SDArray) arrayVar).printXML(pw, pad + (isSingle ? "" : "\t"), constrained);
if (isGrid) {
e = mapVars.elements();
while (e.hasMoreElements()) {
SDArray map = (SDArray) e.nextElement();
//Coverity[DEADCODE]
map.printAsMapXML(pw, pad + (isSingle ? "" : "\t"), constrained);
}
} else {
e = mapVars.elements();
while (e.hasMoreElements()) {
SDArray sda = (SDArray) e.nextElement();
sda.printXML(pw, pad + (isSingle ? "" : "\t"), constrained);
}
}
if (isStructure) {
pw.println(pad + "</Structure>");
} else if (isGrid) {
pw.println(pad + "</Grid>");
}
return;
}
/*
if(isGrid){
os.println(space + getTypeName() + " {" );
os.println(space + " ARRAY:");
((SDArray)arrayVar).printDecl(os, space + (isSingle ? "":" ") , psemi, constrained);
os.println(space + " MAPS:");
for(Enumeration e = mapVars.elements(); e.hasMoreElements(); ) {
SDArray sda = (SDArray)e.nextElement();
sda.printDecl(os, space + (isSingle ? "":" ") , psemi, constrained);
}
os.print(space + "} " + getName());
os.println(";");
}
if(isStructure){
os.println(space + "Structure {");
((SDArray)arrayVar).printDecl(os, space + (isSingle ? "":" ") , psemi, constrained);
for(Enumeration e = mapVars.elements(); e.hasMoreElements(); ) {
SDArray sda = (SDArray)e.nextElement();
sda.printDecl(os, space + (isSingle ? "":" ") , psemi, constrained);
}
os.print(space + "} " + getName());
os.println(";");
}
if(isSingle){
((SDArray)arrayVar).printDecl(os, space + (isSingle ? "":" ") , psemi, constrained);
for(Enumeration e = mapVars.elements(); e.hasMoreElements(); ) {
SDArray sda = (SDArray)e.nextElement();
sda.printDecl(os, space , psemi, constrained);
}
}
*/
}