/**
* @mainpage This is an implementation of
* a Java NeXus API using native methods.
*
* Some changes to the API have been necessary, due to the
* different calling standards between C and Java.
*
* @author Mark Koennecke, 2000 -- 2011
*
* copyright: see accompanying COPYRIGHT file
*
* @see TestJapi.java
* Test program for Java API.
* Illustrates using the #org.nexusformat package
*/
package org.nexusformat;
import java.util.Hashtable;
import java.io.File;
import ncsa.hdf.hdflib.HDFArray;
import ncsa.hdf.hdflib.HDFException;
import ncsa.hdf.hdflib.HDFConstants;
public class NexusFile implements NeXusFileInterface {
// constants
/**
* possible access codes, @see #NexusFile.
*/
public final static int NXACC_READ = 1;
public final static int NXACC_RDWR = 2;
public final static int NXACC_CREATE = 3;
public final static int NXACC_CREATE4 = 4;
public final static int NXACC_CREATE5 = 5;
public final static int NXACC_CREATEXML = 6;
public final static int NXACC_NOSTRIP = 128;
/**
* constant denoting an unlimited dimension.
*/
public final static int NX_UNLIMITED = -1;
/**
* constants for number types. @see #makedata, @see #putattr
* and others.
*/
public final static int NX_FLOAT32 = 5;
public final static int NX_FLOAT64 = 6;
public final static int NX_INT8 = 20;
public final static int NX_BINARY = 20;
public final static int NX_UINT8 = 21;
public final static int NX_BOOLEAN = 21;
public final static int NX_INT16 = 22;
public final static int NX_UINT16 = 23;
public final static int NX_INT32 = 24;
public final static int NX_UINT32 = 25;
public final static int NX_INT64 = 26;
public final static int NX_UINT64 = 27;
public final static int NX_CHAR = 4;
/**
* constants for compression schemes
*/
public final static int NX_COMP_NONE = 100;
/* this one does zlib (deflate), no idea who chose the name */
public final static int NX_COMP_LZW = 200;
public final static int NX_COMP_RLE = 300; /* hdf4 only */
public final static int NX_COMP_HUF = 400; /* hdf4 only */
public final static int NX_COMP_LZW_LVL0 = (100*NX_COMP_LZW + 0);
public final static int NX_COMP_LZW_LVL1 = (100*NX_COMP_LZW + 1);
public final static int NX_COMP_LZW_LVL2 = (100*NX_COMP_LZW + 2);
public final static int NX_COMP_LZW_LVL3 = (100*NX_COMP_LZW + 3);
public final static int NX_COMP_LZW_LVL4 = (100*NX_COMP_LZW + 4);
public final static int NX_COMP_LZW_LVL5 = (100*NX_COMP_LZW + 5);
public final static int NX_COMP_LZW_LVL6 = (100*NX_COMP_LZW + 6);
public final static int NX_COMP_LZW_LVL7 = (100*NX_COMP_LZW + 7);
public final static int NX_COMP_LZW_LVL8 = (100*NX_COMP_LZW + 8);
public final static int NX_COMP_LZW_LVL9 = (100*NX_COMP_LZW + 9);
/**
* Maximum name length, must be VGNAMELENMAX in hlimits.h
*/
protected final static int MAXNAMELEN = 64;
/*
This code takes care of loading the static library required for
this class to work properly. The algorithm first looks for a
property org.nexusformat.JNEXUSLIB and loads that file if available,
else it tries to locate the library in the system shared library
path.
*/
static {
String filename = null;
filename = System.getProperty("org.nexusformat.JNEXUSLIB",null);
if ((filename != null) && (filename.length() > 0)) {
File hdfdll = new File(filename);
if (hdfdll.exists() && hdfdll.canRead() && hdfdll.isFile()) {
System.load(filename);
} else {
throw (new UnsatisfiedLinkError("Invalid JNEXUS library"));
}
} else {
System.loadLibrary("jnexus");
}
}
/**
* This is the handle to the NeXus file handle.
*/
protected int handle;
// Construction
// native methods for this section
protected native int init(String filename, int access);
protected native void close(int handle);
protected native int nxflush(int handle);
/**
* constructs a new NexusFile Object.
* @param filename The name of the NeXus file to access.
* @param access The access mode for the file. Can only be one
* of the predefined NeXus access code NXACC.... These are:
*
* - NXACC_CREATE
*
- or creating a new file.
*
- NXACC_RDWR
*
- For opening an existing file for modification or appending
* data.
*
- NXACC_READ
*
- For opening a file for reading.
*
- NXACC_NOSTRIP
*
- To keep leading and trailing whitespace on strings
*
* @exception NexusException when the file could not be found or
* an HDF error occurred.
*/
public NexusFile(String filename, int access) throws NexusException {
checkForNull(filename);
handle = init(filename,access);
if(handle < 0){
throw new NexusException("Failed to open " + filename);
}
}
/**
* flushes all pending data to disk. Closes any open SDS's.
*/
public void flush() throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
handle = nxflush(handle);
}
/**
* close the NeXus file. To make javalint and diamond happy
* @throws NexusException
*/
public void close() throws NexusException {
if(handle >= 0) {
close(handle);
handle = -1;
}
}
/**
* removes all NeXus file data structures and closes the file. This
* function should automatically be called by the Java garbage
* collector whenever the NexusFile object falls into disuse. However
* the time when this is done is left to the garbage collector. My
* personal experience is that finalize might never be called. I
* suggest, to call finalize yourself when you are done with the
* NeXus file. finalize makes sure that multiple invocations will not
* do any harm.
*/
public void finalize() throws Throwable {
close();
}
// group functions
//native methods for this section
protected native void nxmakegroup(int handle, String name, String nxclass);
protected native void nxopengroup(int handle, String name, String nxclass);
protected native void nxopenpath(int handle, String path);
protected native void nxopengrouppath(int handle, String path);
protected native void nxclosegroup(int handle);
protected native String nxgetpath(int handle);
public void makegroup(String name, String nxclass) throws NexusException {
checkForNull(name, nxclass);
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
nxmakegroup(handle, name, nxclass);
}
public void opengroup(String name, String nxclass) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(name, nxclass);
nxopengroup(handle, name, nxclass);
}
public void openpath(String path) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(path);
nxopenpath(handle,path);
}
public void opengrouppath(String path) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(path);
nxopengrouppath(handle,path);
}
public String getpath() throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
return nxgetpath(handle);
}
public void closegroup() throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
nxclosegroup(handle);
}
// data set handling
// native methods for this section
protected native void nxmakedata(int handle, String name, int type, int rank, int dim[]);
protected native void nxmakedata64(int handle, String name, int type, int rank, long dim[]);
protected native void nxmakecompdata(int handle, String name, int type, int rank, int dim[], int iCompress, int iChunk[]);
protected native void nxmakecompdata64(int handle, String name, int type, int rank, long dim[], int iCompress, long iChunk[]);
protected native void nxopendata(int handle, String name);
protected native void nxclosedata(int handle);
protected native void nxcompress(int handle, int compression_type);
public void compmakedata(String name, int type, int rank, int dim[],
int compression_type, int iChunk[]) throws NexusException {
if (handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkType(type);
checkForNull(name, rank, iChunk);
checkForNegInArray(true, dim, iChunk);
checkCompression(compression_type);
nxmakecompdata(handle, name, type, rank, dim, compression_type, iChunk);
}
public void compmakedata(String name, int type, int rank, long dim[],
int compression_type, long iChunk[]) throws NexusException {
if (handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkType(type);
checkForNull(name, rank, iChunk);
checkForNegInArray(true, dim, iChunk);
checkCompression(compression_type);
nxmakecompdata64(handle, name, type, rank, dim, compression_type, iChunk);
}
public void makedata(String name, int type, int rank, int dim[]) throws
NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkType(type);
checkForNull(name, dim);
checkForNegInArray(true, dim);
nxmakedata(handle, name, type, rank, dim);
}
public void makedata(String name, int type, int rank, long dim[]) throws
NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkType(type);
checkForNull(name, dim);
checkForNegInArray(true, dim);
nxmakedata64(handle, name, type, rank, dim);
}
public void opendata(String name) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(name);
nxopendata(handle,name);
}
public void closedata() throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
nxclosedata(handle);
}
public void compress(int compression_type) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkCompression(compression_type);
nxcompress(handle,compression_type);
}
// data set reading
// native methods in this section
protected native void nxgetdata(int handle, byte bdata[]);
protected native void nxgetslab(int handle, int Start[], int size[], byte bdata[]);
protected native void nxgetslab64(int handle, long Start[], long size[], byte bdata[]);
public void getdata(Object array) throws NexusException {
byte bdata[];
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(array);
try{
HDFArray ha = new HDFArray(array);
bdata = ha.emptyBytes();
nxgetdata(handle,bdata);
array = ha.arrayify(bdata);
}catch(HDFException he) {
throw new NexusException(he.getMessage());
}
}
public void getslab(int start[], int size[], Object array) throws NexusException {
byte bdata[];
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(start, size, array);
checkForNegInArray(false, start, size);
try{
HDFArray ha = new HDFArray(array);
bdata = ha.emptyBytes();
nxgetslab(handle,start,size,bdata);
array = ha.arrayify(bdata);
}catch(HDFException he) {
throw new NexusException(he.getMessage());
}
}
public void getslab(long start[], long size[], Object array) throws NexusException {
byte bdata[];
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(start, size, array);
checkForNegInArray(false, start, size);
try{
HDFArray ha = new HDFArray(array);
bdata = ha.emptyBytes();
nxgetslab64(handle,start,size,bdata);
array = ha.arrayify(bdata);
}catch(HDFException he) {
throw new NexusException(he.getMessage());
}
}
// data set writing
// native methods for this section
protected native void nxputdata(int handle, byte array[]);
protected native void nxputslab(int handle, byte array[], int start[], int size[]);
protected native void nxputslab64(int handle, byte array[], long start[], long size[]);
public void putdata(Object array) throws NexusException {
byte data[];
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(array);
try {
HDFArray ha = new HDFArray(array);
data = ha.byteify();
ha = null;
} catch (HDFException he) {
throw new NexusException(he.getMessage());
}
nxputdata(handle,data);
data = null;
}
public void putslab(Object array, int start[], int size[]) throws NexusException {
byte data[];
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(array, start, size);
checkForNegInArray(false, start, size);
try {
HDFArray ha = new HDFArray(array);
data = ha.byteify();
ha = null;
} catch(HDFException he) {
throw new NexusException(he.getMessage());
}
nxputslab(handle,data,start,size);
data = null;
}
public void putslab(Object array, long start[], long size[]) throws NexusException {
byte data[];
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(array, start, size);
checkForNegInArray(false, start, size);
try {
HDFArray ha = new HDFArray(array);
data = ha.byteify();
ha = null;
} catch(HDFException he) {
throw new NexusException(he.getMessage());
}
nxputslab64(handle,data,start,size);
data = null;
}
// attribute methods
protected native void nxgetattr(int handle, String name, byte bdata[], int args[]);
protected native void nxputattr(int handle, String name, byte array[], int type);
protected native void nxputattra(int handle, String name, byte bdata[], int rank, int dim[], int iType);
protected native int nxgetnextattra(int handle, String[] name, int dim[], int args[]);
protected native void nxgetattra(int handle, String name, byte bdata[]);
protected native void nxgetattrainfo(int handle, String name, int dim[], int args[]);
protected native int nextattr(int handle, String names[], int args[]);
protected native void initattrdir(int handle);
protected native void initgroupdir(int handle);
public Object getattr(String name) throws NexusException {
byte bdata[];
int args[] = new int[2];
int dim[] = new int[32];
int totalsize = 1;
checkForNull(name);
if (handle < 0) throw new NexusException("NAPI-ERROR: File not open");
nxgetattrainfo(handle, name, dim, args);
int rank = args[0];
int type = args[1];
int[] thedims = new int[rank];
for(int i = 0 ; i < rank ; i++) {
thedims[i] = dim[i];
totalsize *= dim[i];
}
Object array = null;
switch (type) {
case NX_CHAR:
case NX_INT8:
array = new byte[totalsize];
break;
case NX_INT16:
array = new short[totalsize];
break;
case NX_INT32:
array = new int[totalsize];
break;
case NX_INT64:
array = new long[totalsize];
break;
case NX_FLOAT32:
array = new float[totalsize];
break;
case NX_FLOAT64:
array = new double[totalsize];
break;
case NX_BOOLEAN:
throw new NexusException("type not currently supported in Java");
case NX_UINT16:
case NX_UINT32:
case NX_UINT64:
throw new NexusException("unsigned types not currently supported in Java");
default:
throw new NexusException("unknown type");
}
try {
HDFArray ha = new HDFArray(array);
bdata = ha.emptyBytes();
nxgetattra(handle, name, bdata);
array = ha.arrayify(bdata);
} catch(HDFException he) {
throw new NexusException(he.getMessage());
}
if (type == NX_CHAR) {
int noofstrings = totalsize / dim[rank-1];
int n = 0;
String strarr[] = new String[noofstrings];
for(int i = 0 ; i < noofstrings ; i++) {
strarr[i] = new String((byte[]) array, i * dim[rank-1], dim[rank-1]);
}
array = strarr;
}
return array;
}
public void getattr(String name, Object array, int args[]) throws NexusException {
byte bdata[];
if (handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkType(args[1]);
checkForNull(name, array, args);
try {
HDFArray ha = new HDFArray(array);
bdata = ha.emptyBytes();
nxgetattr(handle, name, bdata, args);
array = ha.arrayify(bdata);
} catch(HDFException he) {
throw new NexusException(he.getMessage());
}
}
public void putattr(String name, Object array, int iType) throws NexusException {
byte data[];
if (handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkType(iType);
checkForNull(name, array);
try {
HDFArray ha = new HDFArray(array);
data = ha.byteify();
ha = null;
} catch(HDFException he) {
throw new NexusException(he.getMessage());
}
nxputattr(handle,name,data,iType);
data = null;
}
public void putattr(String name, Object array, int size[], int iType) throws NexusException {
byte data[];
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkType(iType);
checkForNull(name, array, size);
try {
HDFArray ha = new HDFArray(array);
data = ha.byteify();
ha = null;
} catch(HDFException he) {
throw new NexusException(he.getMessage());
}
nxputattra(handle, name, data, size.length, size, iType);
data = null;
}
public Hashtable attrdir() throws NexusException {
int args[] = new int[2];
int dim[] = new int[32];
AttributeEntry at;
String names[] = new String[1];
Hashtable h = new Hashtable();
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
initattrdir(handle);
while(nxgetnextattra(handle, names, dim, args) != -1) {
int rank = args[0];
int type = args[1];
String name = names[0];
int length = 1;
int[] thedims = new int[rank];
for(int i = 0 ; i < rank ; i++) {
thedims[i] = dim[i];
length *= dim[i];
}
at = new AttributeEntry();
at.dim = thedims;
at.length = length;
at.type = type;
h.put(name, at);
}
return h;
}
// inquiry
//native methods for this section
protected native void nxgetinfo(int handle, int iDim[], int args[]);
protected native void nxgetinfo64(int handle, long iDim[], int args[]);
protected native void nxsetnumberformat(int handle, int type,
String format);
protected native int nextentry(int handle, String names[]);
public void setnumberformat(int type, String format) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkType(type);
checkForNull(format);
nxsetnumberformat(handle,type,format);
}
public void getinfo(int iDim[], int args[]) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
nxgetinfo(handle,iDim,args);
}
public void getinfo(long iDim[], int args[]) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
nxgetinfo64(handle,iDim,args);
}
public Hashtable groupdir() throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
Hashtable h = new Hashtable();
String names[] = new String[2];
initgroupdir(handle);
while(nextentry(handle,names) != -1) {
h.put(names[0],names[1]);
}
return h;
}
// linking
// native methods for this section
protected native void nxgetgroupid(int handle, NXlink link);
protected native void nxgetdataid(int handle, NXlink link);
protected native void nxmakelink(int handle, NXlink target);
protected native void nxmakenamedlink(int handle, String name, NXlink target);
protected native void nxopensourcepath(int handle);
public NXlink getgroupID() throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
NXlink l = new NXlink();
nxgetgroupid(handle,l);
return l;
}
public NXlink getdataID()throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
NXlink l = new NXlink();
nxgetdataid(handle,l);
return l;
}
public void makelink(NXlink target) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(target);
nxmakelink(handle, target);
}
public void makenamedlink(String name, NXlink target) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(name, target);
nxmakenamedlink(handle, name, target);
}
public void opensourcepath() throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
nxopensourcepath(handle);
}
/**
* checks if any of the arguments is null,
* throws appropriate runtime exception if so
*/
private void checkForNull(Object... args) {
for (Object o : args)
if (o==null) throw new NullPointerException();
}
/**
* checks if any of the ints in the arrays are negative,
* throws appropriate runtime exception if so
*/
private void checkForNegInArray(boolean allowUnlimited, int[]... args) {
for (int[] array : args)
for (int value: array) {
if (value<0)
if (value == this.NX_UNLIMITED && allowUnlimited) {
// all ok this time
} else
throw new IllegalArgumentException("negative dimension received");
}
}
/**
* checks if any of the longs in the arrays are negative,
* throws appropriate runtime exception if so
*/
private void checkForNegInArray(boolean allowUnlimited, long[]... args) {
for (long[] array : args)
for (long value: array) {
if (value<0)
if (value == this.NX_UNLIMITED && allowUnlimited) {
// all ok this time
} else
throw new IllegalArgumentException("negative dimension received");
}
}
/**
* checkType verifies if a parameter is a valid NeXus type code.
* If not an exception is thrown.
* @param type The type value to check.
* @exception NexusException if the the type is no known type value
*/
private void checkType(int type) throws NexusException {
switch(type) {
case NexusFile.NX_FLOAT32:
case NexusFile.NX_FLOAT64:
case NexusFile.NX_INT8:
case NexusFile.NX_UINT8:
case NexusFile.NX_INT16:
case NexusFile.NX_UINT16:
case NexusFile.NX_INT32:
case NexusFile.NX_UINT32:
case NexusFile.NX_INT64:
case NexusFile.NX_UINT64:
case NexusFile.NX_CHAR:
break;
default:
throw new NexusException("Illegal number type requested");
}
}
/**
* checkCompression verifies a parameter is a valid NeXus compression code.
* If not an exception is thrown.
* @param type The value to check.
* @exception NexusException if the the type is no known compression value
*/
private void checkCompression(int compression_type) throws NexusException {
switch(compression_type) {
case NexusFile.NX_COMP_NONE:
case NexusFile.NX_COMP_LZW:
case NexusFile.NX_COMP_RLE:
case NexusFile.NX_COMP_HUF:
case NexusFile.NX_COMP_LZW_LVL0:
case NexusFile.NX_COMP_LZW_LVL1:
case NexusFile.NX_COMP_LZW_LVL2:
case NexusFile.NX_COMP_LZW_LVL3:
case NexusFile.NX_COMP_LZW_LVL4:
case NexusFile.NX_COMP_LZW_LVL5:
case NexusFile.NX_COMP_LZW_LVL6:
case NexusFile.NX_COMP_LZW_LVL7:
case NexusFile.NX_COMP_LZW_LVL8:
case NexusFile.NX_COMP_LZW_LVL9:
break;
default:
throw new NexusException("Invalid compression code requested");
}
}
// external file interface
// native methods for this section
protected native void nxinquirefile(int handle, String names[]);
protected native void nxlinkexternal(int handle, String name, String nxclass, String nxurl);
protected native void nxlinkexternaldataset(int handle, String name, String nxurl);
protected native int nxisexternalgroup(int handle, String name, String nxclass, String nxurl[]);
protected native int nxisexternaldataset(int handle, String name, String nxurl[]);
public String inquirefile() throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
String names[] = new String[1];
nxinquirefile(handle,names);
return names[0];
}
public void linkexternal(String name, String nxclass, String nxurl) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(name, nxclass, nxurl);
nxlinkexternal(handle,name,nxclass,nxurl);
}
public void linkexternaldataset(String name, String nxurl) throws NexusException {
if(handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(name, nxurl);
nxlinkexternaldataset(handle,name,nxurl);
}
public String isexternalgroup(String name, String nxclass) throws NexusException {
if (handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(name, nxclass);
String nxurl[] = new String[1];
int status = nxisexternalgroup(handle,name,nxclass,nxurl);
if (status == 1) {
return nxurl[0];
} else {
return null;
}
}
public String isexternaldataset(String name) throws NexusException {
if (handle < 0) throw new NexusException("NAPI-ERROR: File not open");
checkForNull(name);
String nxurl[] = new String[1];
int status = nxisexternaldataset(handle,name,nxurl);
if (status == 1) {
return nxurl[0];
} else {
return null;
}
}
/**
* debugstop is a debugging helper function which goes into an
* endless loop in the dynamic link library. Then a unix debugger
* may attach to the running java process using the pid, interrupt,
* set the loop variable to leave the loop, set a new breakpoint and
* continue debugging. This works with ladebug on DU40D. This is an
* developer support routine and should NEVER be called in normal
* code.
*/
public native void debugstop();
}