netcdf-c/ncgen/genjjni.c
Dennis Heimbigner 6901206927 Regularize the semantics of mkstemp.
re: https://github.com/Unidata/netcdf-c/issues/1827

The issue is partly resolved by this PR. The proximate problem appears to be that the semantics of mkstemp in **nix is different than the semantics of _mktemp_s in Windows. I had thought they were the same but that is incorrect. The _mktemp_s function will only produce 26 different files and so the netcdf temp file code will fail after about that many iterations.

So, to solve this, I created my own version of mkstemp for windows that uses a random number generator. This appears to solve the reported issue.  I also added the testcase ncdap_test/test_manyurls but made it conditional on --enable-dap-long-tests because it is very slow.

I did note that the provided test program now fails after some 800 iterations with a libcurl error claiming it cannot resolve the host name. My belief is that the library is just running out of resources at this point: too many open curl handles or some such. I doubt if this failure is fixable.

So bottom line is that it is really important to do nc_close when you are finished with a file.

Misc. Other Changes:

1. I took the opportunity to clean up some bad string hacks in the code. Specifically
    * change all uses of strncat to strlcat
    * remove old string hacks: occoncat and occopycat
2. Add heck to see if test.opendap.org is running and if not, then skip test
3. Make CYGWIN use TEMP environment variable
2021-05-14 11:33:03 -06:00

1602 lines
42 KiB
C

/*********************************************************************
* Copyright 2018, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
* $Header: /upc/share/CVS/netcdf-3/ncgen/genjjni.c,v 1.6 2010/04/04 19:39:46 dmh Exp $
*********************************************************************/
#include "includes.h"
#ifdef ENABLE_JAVA
#undef JGDB
#undef TRACE
/*MNEMONIC*/
#define USEMEMORY 1
extern List* vlenconstants; /* List<Constant*>;*/
/* Forward */
static void genjjni_definevardata(Symbol* vsym);
static void genjjni_primattribute(Symbol*, Bytebuffer*, unsigned long);
static void genjjni_scalarprim(Symbol* var, Bytebuffer* code);
static const char* jgroupncid(Symbol*);
static const char* jtypencid(Symbol*);
static const char* jvarncid(Symbol*);
static const char* jdimncid(Symbol*);
#ifdef USE_NETCDF4
static void definejtype(Symbol*);
static char* jprefixed(List* prefix, char* suffix, char* separator);
static void genjjni_deftype(Symbol*);
static void definespecialattributes(Symbol* vsym);
#endif
static int jputvaraprim(Putvar*, Odometer*, Bytebuffer*, size_t*);
static void genjjni_defineattribute(Symbol* asym);
static void genjjni_definevardata(Symbol*);
static void computemaxunlimited(void);
/*
Global Bytebuffer into which to store the C code output;
periodically dumped to file by jflush().
Defined in genjstd.c.
*/
extern Bytebuffer* jcode;
/*
* Generate code for creating netCDF from in-memory structure.
*/
void
gen_ncjava_jni(const char *filename)
{
int idim, ivar, iatt, maxdims;
int ndims, nvars, natts, ngatts, ngrps, ntyps;
char* cmode_string;
#ifdef USE_NETCDF4
int igrp,ityp;
#endif
jcode = bbNew();
bbSetalloc(jcode,C_MAX_STMT);
ndims = listlength(dimdefs);
nvars = listlength(vardefs);
natts = listlength(attdefs);
ngatts = listlength(gattdefs);
ngrps = listlength(grpdefs);
ntyps = listlength(typdefs);
/* Construct the main class */
#ifdef JGDB
jline("import java.io.*;");
#endif
jline("import java.util.*;");
jline("import netcdf.*;");
jline("import static netcdf.Constants.*;");
jline("import static netcdf.Netcdf.*;");
jline("");
jpartial("public class ");
jline(mainname);
jline("{");
/* Do static initializations */
jline("");
jline("static {");
jlined(1,"System.loadLibrary(\"ncjni\");");
jlined(1,"Netcdf.init();");
jline("}");
jline("");
jline("static long memory = Memory.create(); /* For storing data */");
#ifdef USE_NETCDF4
if (ntyps > 0) {
for(ityp = 0; ityp < ntyps; ityp++) {
Symbol* tsym = (Symbol*)listget(typdefs,ityp);
definejtype(tsym);
}
bbCat(jcode,"");
}
jflush();
/*
Define vlen constants; For java, this is done in
two parts. Part 1 (here) defines the constants and part 2
fills them in.
*/
{
jflush(); /* dump code to this point*/
genjjni_vlenconstants(vlenconstants,jcode);
jline("");
}
/* Construct the chunking constants*/
if(!usingclassic) {
for(ivar=0;ivar<nvars;ivar++) {
Bytebuffer* tmp = bbNew();
Symbol* var = (Symbol*)listget(vardefs,ivar);
Specialdata* special = &var->var.special;
if(special->flags & _CHUNKSIZE_FLAG) {
int i;
size_t* chunks = special->_ChunkSizes;
if(special->nchunks == 0 || chunks == NULL) continue;
bbClear(tmp);
for(i=0;i<special->nchunks;i++) {
nprintf(stmt,sizeof(stmt),"%s%ld",
(i == 0?"":", "),special->_ChunkSizes[i]);
bbCat(tmp,stmt);
}
nprintf(stmt,sizeof(stmt),"static final long[] %s_chunksizes = {",
jname(var),special->nchunks);
jpartial(stmt);
jprint(tmp);
jline("} ;");
}
}
jline("");
}
#endif /*USE_NETCDF4*/
jline("static public void");
jline("check_err(int stat) throws Exception");
jline("{");
jlined(1,"if (stat != NC_NOERR) {");
jlined(2,"System.err.println(\"Failed: \"+nc_strerror(stat));");
jlined(2,"throw new Exception(nc_strerror(stat));");
jlined(1,"}");
jline("}");
jline("");
/* Now construct the main procedure*/
jline("static public void main(String[] argv) throws Exception");
nprintf(stmt,sizeof(stmt), "{ /* create %s */",filename);
jline(stmt);
/* create necessary declarations */
jline("");
jlined(1,"int stat; /* return status */");
jlined(1,"int ncid; /* netCDF id */");
jlined(1,"int[] ncidp = new int[1]; /* netCDF id return */");
jflush();
/*
Define vlen constants Part 2.
The idea is to walk all the data lists
whose variable type has a vlen and collect
the vlen data and define a constant for it.
*/
jline("");
jflush(); /* dump code to this point*/
genjjni_vlendata(vlenconstants,jcode);
#ifdef JGDB
jline("{");
jline("System.out.print(\"go?\");");
jline("BufferedReader rin = new BufferedReader(new InputStreamReader(System.in));");
jline("rin.readLine();");
jline("}");
#endif
#ifdef USE_NETCDF4
/* Define variables to hold group ids*/
if(!usingclassic) {
jline("");
jlined(1,"/* group ids */");
}
if(!usingclassic && ngrps > 0) {
for(igrp = 0; igrp < ngrps; igrp++) {
Symbol* gsym = (Symbol*)listget(grpdefs,igrp);
nprintf(stmt,sizeof(stmt),"int %s;",jgroupncid(gsym));
jlined(1,stmt);
}
}
/* define variables to hold type ids*/
if(!usingclassic && ntyps > 0) {
jline("");
jlined(1,"/* type ids */");
for(ityp = 0; ityp < ntyps; ityp++) {
Symbol* tsym = (Symbol*)listget(typdefs,ityp);
nprintf(stmt,sizeof(stmt),"int %s;",jtypencid(tsym));
jlined(1,stmt);
}
}
jflush();
#endif
if (ndims > 0) {
jline("");
jlined(1,"/* dimension ids */");
for(idim = 0; idim < ndims; idim++) {
Symbol* dsym = (Symbol*)listget(dimdefs,idim);
nprintf(stmt,sizeof(stmt),"int %s;",jdimncid(dsym));
jlined(1,stmt);
}
jline("");
jlined(1,"/* dimension lengths */");
for(idim = 0; idim < ndims; idim++) {
Symbol* dsym = (Symbol*)listget(dimdefs,idim);
if (dsym->dim.size == NC_UNLIMITED) {
nprintf(stmt,sizeof(stmt),"%sfinal long %s_len = NC_UNLIMITED;",
indented(1),jname(dsym));
} else {
nprintf(stmt,sizeof(stmt),"%sfinal long %s_len = %lu;",
indented(1),
jname(dsym),
(unsigned long) dsym->dim.size);
}
jline(stmt);
}
}
jflush();
maxdims = 0; /* most dimensions of any variable */
for(ivar = 0; ivar < nvars; ivar++) {
Symbol* vsym = (Symbol*)listget(vardefs,ivar);
if(vsym->typ.dimset.ndims > maxdims)
maxdims = vsym->typ.dimset.ndims;
}
if (nvars > 0) {
jline("");
jlined(1,"/* variable ids */");
for(ivar = 0; ivar < nvars; ivar++) {
Symbol* vsym = (Symbol*)listget(vardefs,ivar);
nprintf(stmt,sizeof(stmt),"int %s;", jvarncid(vsym));
jlined(1,stmt);
}
jline("");
jlined(1,"/* rank (number of dimensions) for each variable */");
for(ivar = 0; ivar < nvars; ivar++) {
Symbol* vsym = (Symbol*)listget(vardefs,ivar);
nprintf(stmt,sizeof(stmt),"final int RANK_%s = %d;",
jname(vsym),vsym->typ.dimset.ndims);
jlined(1,stmt);
}
if (maxdims > 0) { /* we have dimensioned variables */
jline("");
jlined(1,"/* variable shapes */");
for(ivar = 0; ivar < nvars; ivar++) {
Symbol* vsym = (Symbol*)listget(vardefs,ivar);
if (vsym->typ.dimset.ndims > 0) {
nprintf(stmt,sizeof(stmt),"final int[] %s_dims = new int[RANK_%s];",
jname(vsym), jname(vsym));
jlined(1,stmt);
}
}
}
}
jflush();
/* create netCDF file, uses NC_CLOBBER mode */
jline("");
jlined(1,"/* enter define mode */");
if (!cmode_modifier) {
cmode_string = "NC_CLOBBER";
} else if (cmode_modifier & NC_64BIT_OFFSET) {
cmode_string = "NC_CLOBBER|NC_64BIT_OFFSET";
#ifdef USE_NETCDF4
} else if (cmode_modifier & NC_CLASSIC_MODEL) {
cmode_string = "NC_CLOBBER|NC_NETCDF4|NC_CLASSIC_MODEL";
} else if (cmode_modifier & NC_NETCDF4) {
cmode_string = "NC_CLOBBER|NC_NETCDF4";
#endif
} else {
derror("unknown cmode modifier");
cmode_string = "NC_CLOBBER";
}
nprintf(stmt,sizeof(stmt),"check_err(nc_create(\"%s\", %s, ncidp));",
filename,cmode_string);
jlined(1,stmt);
jlined(1,"ncid = ncidp[0];");
jflush();
#ifdef USE_NETCDF4
/* Define the group structure */
/* ncid created above is also root group*/
if(!usingclassic) {
nprintf(stmt,sizeof(stmt),"%s = ncid;",jgroupncid(rootgroup));
jlined(1,stmt);
/* walking grpdefs list will do a preorder walk of all defined groups*/
for(igrp=0;igrp<listlength(grpdefs);igrp++) {
Symbol* gsym = (Symbol*)listget(grpdefs,igrp);
if(gsym == rootgroup) continue; /* ignore root*/
if(gsym->container == NULL)
PANIC("null container");
nprintf(stmt,sizeof(stmt),
"check_err(nc_def_grp(%s, \"%s\", ncidp));",
jgroupncid(gsym->container),
gsym->name);
jlined(1,stmt);
nprintf(stmt,sizeof(stmt),"%s = ncidp[0];",jgroupncid(gsym));
jlined(1,stmt);
}
jflush();
}
#endif
#ifdef USE_NETCDF4
/* Construct code to define types*/
if(ntyps > 0) {
jline("");
for(ityp = 0; ityp < ntyps; ityp++) {
Symbol* tsym = (Symbol*)listget(typdefs,ityp);
if(tsym->subclass == NC_PRIM
|| tsym->subclass == NC_ARRAY) continue; /* no need to do these*/
genjjni_deftype(tsym);
jline("");
}
}
jflush();
#endif
/* define dimensions from info in dims array */
if (ndims > 0) {
jline("");
jlined(1,"/* define dimensions */");
for(idim = 0; idim < ndims; idim++) {
Symbol* dsym = (Symbol*)listget(dimdefs,idim);
nprintf(stmt,sizeof(stmt),
"check_err(nc_def_dim(%s, \"%s\", %s_len, ncidp));",
jgroupncid(dsym->container),
jescapifyname(dsym->name), jname(dsym));
jlined(1,stmt);
nprintf(stmt,sizeof(stmt),"%s = ncidp[0];",jdimncid(dsym));
jlined(1,stmt);
}
}
jflush();
/* define variables from info in vars array */
if (nvars > 0) {
jline("");
jlined(1,"/* define variables */");
for(ivar = 0; ivar < nvars; ivar++) {
Symbol* vsym = (Symbol*)listget(vardefs,ivar);
Symbol* basetype = vsym->typ.basetype;
Dimset* dimset = &vsym->typ.dimset;
jline("");
if(dimset->ndims > 0) {
for(idim = 0; idim < dimset->ndims; idim++) {
Symbol* dsym = dimset->dimsyms[idim];
nprintf(stmt,sizeof(stmt),
"%s_dims[%d] = %s;",
jname(vsym),
idim,
jdimncid(dsym));
jlined(1,stmt);
}
}
nprintf(stmt,sizeof(stmt),
"check_err(nc_def_var(%s, \"%s\", %s, RANK_%s, %s, ncidp));",
jgroupncid(vsym->container),
jescapifyname(vsym->name),
jtypencid(basetype),
jname(vsym),
(dimset->ndims == 0?"null":poolcat(jname(vsym),"_dims")));
jlined(1,stmt);
nprintf(stmt,sizeof(stmt),"%s = ncidp[0];",jvarncid(vsym));
jlined(1,stmt);
#ifdef USE_NETCDF4
definespecialattributes(vsym);
#endif /*USE_NETCDF4*/
}
}
jflush();
/* Define the global attributes*/
if(ngatts > 0) {
jline("");
jlined(1,"/* assign global attributes */");
for(iatt = 0; iatt < ngatts; iatt++) {
Symbol* gasym = (Symbol*)listget(gattdefs,iatt);
genjjni_defineattribute(gasym);
}
jline("");
}
jflush();
/* Define the variable specific attributes*/
if(natts > 0) {
jline("");
jlined(1,"/* assign per-variable attributes */");
for(iatt = 0; iatt < natts; iatt++) {
Symbol* asym = (Symbol*)listget(attdefs,iatt);
genjjni_defineattribute(asym);
}
jline("");
}
jflush();
if (nofill_flag) {
jlined(1,"/* don't initialize variables with fill values */");
jlined(1,"check_err(nc_set_fill(%s, NC_NOFILL, 0));");
}
jline("");
jlined(1,"/* leave define mode */");
nprintf(stmt,sizeof(stmt),"check_err(nc_enddef(%s));",
jgroupncid(rootgroup));
jlined(1,stmt);
jflush();
/* Load values into those variables with defined data */
if(nvars > 0) {
jline("");
jlined(1,"/* assign variable data */");
for(ivar = 0; ivar < nvars; ivar++) {
Symbol* vsym = (Symbol*)listget(vardefs,ivar);
if(vsym->data != NULL) genjjni_definevardata(vsym);
}
jline("");
/* compute the max actual size of the unlimited dimension*/
if(usingclassic) computemaxunlimited();
}
jflush();
}
void
cl_java_jni(void)
{
nprintf(stmt,sizeof(stmt),"check_err(nc_close(%s));",
jgroupncid(rootgroup));
jlined(1,stmt);
jline("}"); /* main */
jline("}"); /* class Main */
jflush();
}
#ifdef USE_NETCDF4
static void
definespecialattributes(Symbol* vsym)
{
Specialdata* special = &vsym->var.special;
if(usingclassic) return;
if(special->flags & _STORAGE_FLAG) {
int storage = special->_Storage;
size_t* chunks = special->_ChunkSizes;
nprintf(stmt,sizeof(stmt),
"%scheck_err(nc_def_var_chunking(%s, %s, %s, ",
indented(1),
jgroupncid(vsym->container),
jvarncid(vsym),
(storage == NC_CONTIGUOUS?"NC_CONTIGUOUS":"NC_CHUNKED"));
jpartial(stmt);
if(special->nchunks == 0 || chunks == NULL)
jpartial("new long[]{}");
else {
nprintf(stmt,sizeof(stmt),"%s_chunksizes",jname(vsym));
jpartial(stmt);
}
jline("));");
}
if(special->flags & _FLETCHER32_FLAG) {
nprintf(stmt,sizeof(stmt),
"check_err(nc_def_var_fletcher32(%s, %s, %d));",
jgroupncid(vsym->container),
jvarncid(vsym),
special->_Fletcher32);
jlined(1,stmt);
}
if(special->flags & (_DEFLATE_FLAG | _SHUFFLE_FLAG)) {
nprintf(stmt,sizeof(stmt),
"check_err(nc_def_var_deflate(%s, %s, %s, %d, %d));",
jgroupncid(vsym->container),
jvarncid(vsym),
(special->_Shuffle == 1?"NC_SHUFFLE":"NC_NOSHUFFLE"),
(special->_DeflateLevel >= 0?1:0),
(special->_DeflateLevel >= 0?special->_DeflateLevel:0));
jlined(1,stmt);
}
if(special->flags & _ENDIAN_FLAG) {
nprintf(stmt,sizeof(stmt),
"check_err(nc_def_var_endian(%s, %s, %s));",
jgroupncid(vsym->container),
jvarncid(vsym),
(special->_Endianness == NC_ENDIAN_LITTLE?"NC_ENDIAN_LITTLE"
:"NC_ENDIAN_BIG")
);
jlined(1,stmt);
}
if(special->flags & _NOFILL_FLAG) {
nprintf(stmt,sizeof(stmt),
"check_err(nc_def_var_fill(%s, %s, %s));",
jgroupncid(vsym->container),
jvarncid(vsym),
(special->_Fill?"NC_FILL":"NC_NOFILL")
);
jlined(1,stmt);
}
}
#endif /*USE_NETCDF4*/
/*
* Return java type name for netCDF type, given type code.
*/
const char*
jtype(nc_type type)
{
switch (type) {
case NC_CHAR: return "char";
case NC_BYTE: return "byte";
case NC_SHORT: return "short";
case NC_INT: return "int";
case NC_FLOAT: return "float";
case NC_DOUBLE: return "double";
case NC_UBYTE: return "long";
case NC_USHORT: return "long";
case NC_UINT: return "long";
case NC_INT64: return "long";
case NC_UINT64: return "long";
case NC_STRING: return "String";
case NC_ENUM: return "int";
case NC_OPAQUE: return "String";
default: PANIC1("ncctype: bad type code:%d",type);
}
return 0;
}
/*
* Return a type name and dimensions for constant arrays
* for netCDF type, given type code.
*/
const char*
jarraytype(nc_type type)
{
switch (type) {
case NC_CHAR:
return "String";
case NC_BYTE:
return "byte";
case NC_SHORT:
return "short";
case NC_INT:
return "int";
case NC_FLOAT:
return "float";
case NC_DOUBLE:
return "double";
case NC_UBYTE:
return "long";
case NC_USHORT:
return "long";
case NC_UINT:
return "long";
case NC_INT64:
return "long";
case NC_UINT64:
return "long";
case NC_STRING:
return "String";
case NC_ENUM:
return "int";
case NC_OPAQUE:
return "String";
default:
PANIC1("ncctype: bad type code:%d",type);
}
return 0;
}
/*
* Return netcdf interface type name for netCDF type suffix, given type code.
*/
const char*
jstype(nc_type nctype)
{
switch (nctype) {
case NC_CHAR:
return "text";
case NC_BYTE:
return "schar";
case NC_SHORT:
return "short";
case NC_INT:
return "int";
case NC_FLOAT:
return "float";
case NC_DOUBLE:
return "double";
case NC_UBYTE:
return "ubyte";
case NC_USHORT:
return "ushort";
case NC_UINT:
return "uint";
case NC_INT64:
return "longlong";
case NC_UINT64:
return "ulonglong";
case NC_STRING:
return "string";
case NC_OPAQUE:
return "opaque";
case NC_ENUM:
return "enum";
default:
derror("ncstype: bad type code: %d",nctype);
return 0;
}
}
/* Return the group name for the specified group*/
static const char*
jgroupncid(Symbol* sym)
{
#ifdef USE_NETCDF4
if(usingclassic) {
return "ncid";
} else {
char* grptmp;
const char* tmp1;
if(sym == NULL) return jgroupncid(rootgroup);
ASSERT(sym->objectclass == NC_GRP);
tmp1 = jname(sym);
grptmp = poolalloc(strlen(tmp1)+strlen("_grp")+1);
strcpy(grptmp,tmp1);
strcat(grptmp,"_grp");
return grptmp;
}
#else
return "ncid";
#endif
}
/* Compute the name for a given type's id*/
/* Watch out: the result is a pool alloc*/
static const char*
jtypencid(Symbol* tsym)
{
char* typtmp;
const char* tmp1;
if(tsym->subclass == NC_PRIM)
return nctype(tsym->typ.typecode);
tmp1 = jtypename(tsym);
typtmp = poolalloc(strlen(tmp1)+strlen("_typ")+1);
strcpy(typtmp,tmp1);
strcat(typtmp,"_typ");
return typtmp;
}
/* Compute the name for a given var's id*/
/* Watch out: the result is a static*/
static const char*
jvarncid(Symbol* vsym)
{
const char* tmp1;
char* vartmp;
tmp1 = jname(vsym);
vartmp = poolalloc(strlen(tmp1)+strlen("_id")+1);
strcpy(vartmp,tmp1);
strcat(vartmp,"_id");
return vartmp;
}
/* Compute the name for a given dim's id*/
/* Watch out: the result is a static*/
static const char*
jdimncid(Symbol* dsym)
{
const char* tmp1;
char* dimtmp;
tmp1 = jname(dsym);
dimtmp = poolalloc(strlen(tmp1)+strlen("_dim")+1);
strcpy(dimtmp,tmp1);
strcat(dimtmp,"_dim");
return dimtmp;
}
/* Compute the C name for a given type*/
const char*
jtypename(Symbol* tsym)
{
const char* name;
ASSERT(tsym->objectclass == NC_TYPE);
if(tsym->subclass == NC_PRIM)
name = jtype(tsym->typ.typecode);
else
name = jname(tsym);
return name;
}
#ifdef USE_NETCDF4
/*
Only generate type info for enumerations
*/
static void
definejtype(Symbol* tsym)
{
int i;
int nconst;
ASSERT(tsym->objectclass == NC_TYPE);
if(tsym->subclass != NC_ENUM) return;
/* start enum def */
nprintf(stmt,sizeof(stmt),"enum %s {",jtypename(tsym));
jline(stmt);
/* define the constants */
nconst = listlength(tsym->subnodes);
for(i=0;i<nconst;i++) {
Symbol* econst = (Symbol*)listget(tsym->subnodes,i);
ASSERT(econst->subclass == NC_ECONST);
nprintf(stmt,sizeof(stmt),"%s%s(%s)%s",
indented(1),
jtypename(econst),
jconst(econst->typ.econst),
(i == (nconst - 1)?";":","));
jlined(1,stmt);
}
/* add boilerplate */
nprintf(stmt,sizeof(stmt),"private final %s value;",
jtypename(tsym->typ.basetype));
jlined(1,stmt);
nprintf(stmt,sizeof(stmt),"%s(%s value) {this.value = value;}",
jtypename(tsym),jtypename(tsym->typ.basetype));
jlined(1,stmt);
nprintf(stmt,sizeof(stmt),"public %s value() {return this.value;}",
jtypename(tsym->typ.basetype));
jlined(1,stmt);
jline("}");
}
/*
Generate the code for defining a given type
*/
static void
genjjni_deftype(Symbol* tsym)
{
int i;
ASSERT(tsym->objectclass == NC_TYPE);
switch (tsym->subclass) {
case NC_PRIM: break; /* these are already taken care of*/
case NC_OPAQUE:
nprintf(stmt,sizeof(stmt),"check_err(nc_def_opaque(%s, %lu, \"%s\", ncidp));",
jgroupncid(tsym->container),
tsym->typ.size,
jescapifyname(tsym->name));
jlined(1,stmt);
nprintf(stmt,sizeof(stmt),"%s = ncidp[0];",jtypencid(tsym));
jlined(1,stmt);
break;
case NC_ENUM:
jlined(1,"{");
jlined(1,stmt);
jlined(1,"long econst;");
nprintf(stmt,sizeof(stmt),"check_err(nc_def_enum(%s, %s, \"%s\", ncidp));",
jgroupncid(tsym->container),
nctype(tsym->typ.basetype->typ.typecode),
jname(tsym));
jlined(1,stmt);
nprintf(stmt,sizeof(stmt),"%s = ncidp[0];",jtypencid(tsym));
jlined(1,stmt);
for(i=0;i<listlength(tsym->subnodes);i++) {
Symbol* econst = (Symbol*)listget(tsym->subnodes,i);
ASSERT(econst->subclass == NC_ECONST);
nprintf(stmt,sizeof(stmt),"econst = (long)%s.%s.value();",
jname(tsym),
jname(econst));
jlined(1,stmt);
nprintf(stmt,sizeof(stmt),"check_err(nc_insert_enum(%s, %s, \"%s\", econst));",
jgroupncid(tsym->container), jtypencid(tsym),
jescapifyname(econst->name));
jlined(1,stmt);
}
jlined(1,"}");
break;
case NC_VLEN:
nprintf(stmt,sizeof(stmt),"stat = nc_def_vlen(%s, \"%s\", %s, ncidp);",
jgroupncid(tsym->container),
jescapifyname(tsym->name),
jtypencid(tsym->typ.basetype));
jlined(1,stmt);
nprintf(stmt,sizeof(stmt),"%s = ncidp[0];",jtypencid(tsym));
jlined(1,stmt);
break;
case NC_COMPOUND:
/* We cannot use sizeof(), so use the computed size */
nprintf(stmt,sizeof(stmt),"check_err(nc_def_compound(%s, %ld, \"%s\", ncidp));",
jgroupncid(tsym->container),
(unsigned long)tsym->typ.size,
jtypename(tsym),
tsym->name);
jlined(1,stmt);
nprintf(stmt,sizeof(stmt),"%s = ncidp[0];",jtypencid(tsym));
jlined(1,stmt);
/* Generate the field dimension constants*/
jlined(1,"{");
for(i=0;i<listlength(tsym->subnodes);i++) {
int j;
Symbol* efield = (Symbol*)listget(tsym->subnodes,i);
ASSERT(efield->subclass == NC_FIELD);
if(efield->typ.dimset.ndims == 0) continue;
nprintf(stmt,sizeof(stmt),"final int %s_dims[] = new int[] {",
jname(efield));
for(j=0;j<efield->typ.dimset.ndims;j++) {
char tmp[256];
Symbol* e = efield->typ.dimset.dimsyms[j];
ASSERT(e->dim.isconstant);
sprintf(tmp,"%u",e->dim.size);
strcat(stmt,(j==0?"":", "));
strcat(stmt,tmp);
}
strcat(stmt,"};");
jlined(1,stmt);
}
for(i=0;i<listlength(tsym->subnodes);i++) {
Symbol* efield = (Symbol*)listget(tsym->subnodes,i);
char tmp[1024];
ASSERT(efield->subclass == NC_FIELD);
sprintf(tmp,"%lu",efield->typ.offset);
if(efield->typ.dimset.ndims > 0){
nprintf(stmt,sizeof(stmt),"check_err(nc_insert_array_compound(%s, %s, \"%s\", %s, %s, %d, %s_dims));",
jgroupncid(tsym->container),
jtypencid(tsym),
jescapifyname(efield->name),
tmp,
jtypencid(efield->typ.basetype),
efield->typ.dimset.ndims,
jname(efield));
} else {
nprintf(stmt,sizeof(stmt),"check_err(nc_insert_compound(%s, %s, \"%s\", %s, %s));",
jgroupncid(tsym->container),
jtypencid(tsym),
jescapifyname(efield->name),
tmp,
jtypencid(efield->typ.basetype));
}
jlined(1,stmt);
}
jlined(1,"}");
break;
case NC_ARRAY:
/* ignore: this will be handled by def_var*/
break;
default: panic("genjjni_deftype: unexpected type subclass: %d",tsym->subclass);
}
}
#endif
static void
genjjni_defineattribute(Symbol* asym)
{
unsigned long len;
Datalist* list;
Symbol* basetype = asym->typ.basetype;
Bytebuffer* code = NULL; /* capture other decls*/
list = asym->data;
if(list == NULL) PANIC("empty attribute list");
len = asym->att.count;
if(len == 0) PANIC("empty attribute list");
nprintf(stmt,sizeof(stmt),"/* attribute: %s */",asym->name);
jlined(1,stmt);
code = bbNew();
genjjni_attrdata(asym,code);
/* Handle primitives separately */
if(isprimplus(basetype->typ.typecode)) {
if(basetype->typ.typecode != NC_CHAR) commify(code);
genjjni_primattribute(asym, code, len);
} else { /* User defined type */
#ifndef USE_NETCDF4
verror("Non-classic type: %s",nctypename(basetype->typ.typecode));
#else /* USE_NETCDF4 */
if(usingclassic && !isclassicprim(basetype->typ.typecode)) {
verror("Non-classic type: %s",nctypename(basetype->typ.typecode));
return;
}
/* Dump the generation code */
jprint(code);
nprintf(stmt,sizeof(stmt),"check_err(nc_put_att_memory(%s, %s, \"%s\", %s, %lu, memory));",
jgroupncid(asym->container),
(asym->att.var == NULL?"NC_GLOBAL"
:jvarncid(asym->att.var)),
jescapifyname(asym->name),
jtypencid(basetype),
len);
jlined(1,stmt);
#endif
}
bbFree(code);
}
static void
genjjni_primattribute(Symbol* asym, Bytebuffer* code, unsigned long len)
{
Symbol* basetype = asym->typ.basetype;
nc_type typecode = basetype->typ.typecode;
/* Handle NC_CHAR specially */
if(typecode == NC_CHAR) {
/* revise the length count */
len = bbLength(code);
if(len == 0) {bbAppend(code,'\0'); len++;}
jquotestring(code,'"');
} else {
/* Convert to constant */
char* code2 = bbDup(code);
bbClear(code);
nprintf(stmt,sizeof(stmt),"new %s[]",
jarraytype(typecode));
bbCat(code,stmt);
bbCat(code,"{");
bbCat(code,code2);
bbCat(code,"}");
efree(code2);
}
switch (typecode) {
case NC_BYTE:
case NC_SHORT:
case NC_INT:
case NC_FLOAT:
case NC_DOUBLE:
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_att_%s(%s, %s, \"%s\", %s, %lu, ",
indented(1),
jstype(basetype->typ.typecode),
jgroupncid(asym->container),
(asym->att.var == NULL?"NC_GLOBAL"
:jvarncid(asym->att.var)),
jescapifyname(asym->name),
jtypencid(basetype),
len);
jpartial(stmt);
jprint(code);
jline("));");
jflush();
break;
case NC_CHAR:
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_att_%s(%s, %s, \"%s\", %lu, ",
indented(1),
jstype(basetype->typ.typecode),
jgroupncid(asym->container),
(asym->att.var == NULL?"NC_GLOBAL"
:jvarncid(asym->att.var)),
jescapifyname(asym->name),
len);
jpartial(stmt);
jprint(code);
jline("));");
jflush();
break;
#ifdef USING_NETCDF4
/* !usingclassic only (except NC_STRING) */
case NC_UBYTE:
case NC_USHORT:
case NC_UINT:
case NC_INT64:
case NC_UINT64:
case NC_STRING:
if(usingclassic) {
verror("Non-classic type: %s",njtypename(basetype->typ.typecode));
return;
}
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_att_%s(%s, %s, \"%s\", %s, %lu, ",
indented(1),
jstype(basetype->typ.typecode),
jgroupncid(asym->container),
(asym->att.var == NULL?"NC_GLOBAL"
:jvarncid(asym->att.var)),
jescapifyname(asym->name),
jtypencid(basetype),
len);
jpartial(stmt);
jprint(code);
jline("));");
jflush();
break;
#endif
default: break;
}
}
static void
computemaxunlimited(void)
{
int i;
unsigned long maxsize;
Symbol* udim = rootgroup->grp.unlimiteddim;
if(udim == NULL) return; /* there is no unlimited dimension*/
/* Look at each variable and see what*/
/* size it gives to the unlimited dim (if any)*/
maxsize = 0;
for(i=0;i<listlength(vardefs);i++) {
Symbol* dim;
Symbol* var = (Symbol*)listget(vardefs,i);
if(var->typ.dimset.ndims == 0) continue; /* rank == 0*/
dim = var->typ.dimset.dimsyms[0];
if(dim->dim.size != NC_UNLIMITED) continue; /* var does not use unlimited*/
if(var->typ.dimset.dimsyms[0]->dim.size > maxsize)
maxsize = var->typ.dimset.dimsyms[0]->dim.size;
}
}
/* Define the put_vara closure function for C data*/
/*
Important assumptions:
1. The leftmost changed index controls the count set.
2. All indices to the right of #1 are assumed to be at there
max values.
*/
static int
jputvara(struct Putvar* closure, Odometer* odom, Bytebuffer* databuf)
{
int i;
int stat = NC_NOERR;
size_t startset[NC_MAX_VAR_DIMS];
size_t countset[NC_MAX_VAR_DIMS];
Symbol* vsym = closure->var;
Symbol* basetype = vsym->typ.basetype;
Dimset* dimset = &vsym->typ.dimset;
Bytebuffer* code = closure->code;
char dimstring[NC_MAX_VAR_DIMS*2+1];
size_t count;
nc_type typecode = basetype->typ.typecode;
memset((void*)startset,0,sizeof(startset));
memset((void*)countset,0,sizeof(countset));
#ifdef TRACE
fprintf(stderr,"putvara: %s: ",vsym->name);
fprintf(stderr,"odom = %s\n",odometerprint(odom));
fprintf(stderr,"initial startset = [");
for(i=0;i<closure->rank;i++) fprintf(stderr,"%s%u",(i>0?", ":""),closure->startset[i]);
fprintf(stderr,"]\n");
fflush(stderr);
#endif
/* Compute base on a change in the 0th dimension*/
for(i=1;i<closure->rank;i++) {
startset[i] = 0;
countset[i] = odom->dims[i].datasize;
}
startset[0] = closure->startset[0];
countset[0] = odom->dims[0].index - startset[0];
#ifdef TRACE
{
fprintf(stderr,"startset = [");
for(i=0;i<closure->rank;i++) fprintf(stderr,"%s%u",(i>0?", ":""),startset[i]);
fprintf(stderr,"] ");
fprintf(stderr,"countset = [");
for(i=0;i<closure->rank;i++) fprintf(stderr,"%s%u",(i>0?", ":""),countset[i]);
fprintf(stderr,"]\n");
fflush(stderr);
}
#endif
/* generate constants for data*/
count = 1;
dimstring[0] = '\0';
for(i=0;i<dimset->ndims;i++) {
count *= countset[i];
strcat(dimstring,"[]");
}
/* define a block to avoid name clashes*/
nprintf(stmt,sizeof(stmt),"%s{\n",indented(1));
bbCat(code,stmt);
/* generate constants for startset, countset*/
nprintf(stmt,sizeof(stmt),"%slong[] %s_startset = new long[] {",
indented(1),
jname(vsym));
bbCat(code,stmt);
for(i=0;i<closure->rank;i++) {
nprintf(stmt,sizeof(stmt),"%s%lu",(i>0?", ":""),startset[i]);
bbCat(code,stmt);
}
bbCat(code,"} ;\n");
nprintf(stmt,sizeof(stmt),"%slong[] %s_countset = new long[] {",
indented(1),
jname(vsym));
bbCat(code,stmt);
for(i=0;i<closure->rank;i++) {
nprintf(stmt,sizeof(stmt),"%s%lu",(i>0?", ":""),countset[i]);
bbCat(code,stmt);
}
bbCat(code,"} ;\n");
/* Handle primitives separately */
if(isprimplus(typecode)) {
jputvaraprim(closure, odom,databuf,countset);
} else { /* User defined type */
#ifndef USE_NETCDF4
verror("Non-classic type: %s",nctypename(basetype->typ.typecode));
#else /* USE_NETCDF4 */
if(usingclassic && !isclassicprim(basetype->typ.typecode)) {
verror("Non-classic type: %s",nctypename(basetype->typ.typecode));
return NC_EINVAL;
}
/* Dump the generation code */
bbCat(code,bbContents(databuf));
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_vara_memory(%s, %s, %s_startset, %s_countset, memory));\n",
indented(1),
jgroupncid(vsym->container),
jvarncid(vsym),
jname(vsym),
jname(vsym));
bbCat(code,stmt);
#endif
}
/* end defined block*/
nprintf(stmt,sizeof(stmt),"%s}\n",indented(1));
bbCat(code,stmt);
for(i=0;i<closure->rank;i++) {
closure->startset[i] = startset[i] + countset[i];
}
bbClear(databuf);
return stat;
}
static int
jputvaraprim(struct Putvar* closure, Odometer* odom, Bytebuffer* databuf, size_t* countset)
{
int i;
Symbol* vsym = closure->var;
Symbol* basetype = vsym->typ.basetype;
Dimset* dimset = &vsym->typ.dimset;
Bytebuffer* code = closure->code;
size_t len;
nc_type typecode = basetype->typ.typecode;
/* generate constants for data*/
len = 1;
for(i=0;i<dimset->ndims;i++) {
len *= countset[i];
}
/* Handle NC_CHAR specially */
if(typecode == NC_CHAR) {
/* revise the length count */
len = bbLength(databuf);
if(len == 0) {bbAppend(databuf,'\0'); len++;}
jquotestring(databuf,'"');
} else {
/* Convert to constant */
char* databuf2;
commify(databuf);
databuf2 = bbDup(databuf);
bbClear(databuf);
nprintf(stmt,sizeof(stmt),"new %s[]",
jarraytype(typecode));
bbCat(databuf,stmt);
bbCat(databuf,"{");
bbCat(databuf,databuf2);
bbCat(databuf,"}");
efree(databuf2);
}
switch (typecode) {
case NC_BYTE:
case NC_SHORT:
case NC_INT:
case NC_FLOAT:
case NC_DOUBLE:
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_vara_%s(%s, %s, %s_startset, %s_countset, ",
indented(1),
jstype(basetype->typ.typecode),
jgroupncid(vsym->container),
jvarncid(vsym),
jname(vsym),
jname(vsym));
bbCat(code,stmt);
bbCat(code,bbContents(databuf));
bbCat(code,"));\n");
break;
case NC_CHAR:
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_vara_%s(%s, %s, %s_startset, %s_countset, ",
indented(1),
jstype(basetype->typ.typecode),
jgroupncid(vsym->container),
jvarncid(vsym),
jname(vsym),
jname(vsym));
bbCat(code,stmt);
bbCat(code,bbContents(databuf));
bbCat(code,"));\n");
break;
#ifdef USE_NETCDF4
/* !usingclassic only (except NC_STRING) */
case NC_UBYTE:
case NC_USHORT:
case NC_UINT:
case NC_INT64:
case NC_UINT64:
case NC_STRING:
if(usingclassic) {
verror("Non-classic type: %s",nctypename(typecode));
return NC_EINVAL;
}
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_vara_%s(%s, %s, %s_startset, %s_countset, ",
indented(1),
jstype(basetype->typ.typecode),
jgroupncid(vsym->container),
jvarncid(vsym),
jname(vsym),
jname(vsym));
bbCat(code,stmt);
bbCat(code,bbContents(databuf));
bbCat(code,"));\n");
break;
case NC_ENUM:
case NC_OPAQUE:
if(usingclassic) {
verror("Non-classic type: %s",nctypename(typecode));
return NC_EINVAL;
}
/* We use some augmented vara procedures */
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_vara_%s(%s, %s, %s_startset, %s_countset, ",
indented(1),
(typecode == NC_ENUM?"enum":"opaque"),
jgroupncid(vsym->container),
jvarncid(vsym),
jname(vsym),
jname(vsym));
bbCat(code,stmt);
bbCat(code,bbContents(databuf));
bbCat(code,"));\n");
break;
#endif
default:
verror("Non-classic type: %s",nctypename(typecode));
}
bbClear(databuf);
return NC_NOERR;
}
static void
genjjni_definevardata(Symbol* vsym)
{
Dimset* dimset = &vsym->typ.dimset;
Symbol* basetype = vsym->typ.basetype;
int rank = dimset->ndims;
int isscalar = (dimset->ndims == 0);
Bytebuffer* code;
nc_type typecode = basetype->typ.typecode;
if(vsym->data == NULL) return;
code = bbNew();
/* Handle special cases first*/
if(isscalar) {
genjjni_scalardata(vsym,code);
if(isprimplus(typecode)) {
genjjni_scalarprim(vsym,code);
} else {
nprintf(stmt,sizeof(stmt),"check_err(nc_put_var_memory(%s, %s, memory));",
jgroupncid(vsym->container),
jvarncid(vsym));
jlined(1,stmt);
}
} else { /* Non-scalar*/
Bytebuffer* databuf;
/* build a closure*/
Putvar closure;
closure.putvar = jputvara;
closure.rank = rank;
closure.code = code;
closure.var = vsym;
memset(closure.startset,0,sizeof(closure.startset));
/* Most complex case; use closure as needed*/
/* Use a separate data buffer to capture the data list*/
databuf = bbNew();
genjjni_arraydata(vsym,&closure,databuf);
jprint(code);
bbFree(databuf);
}
bbFree(code);
}
static void
genjjni_scalarprim(Symbol* var, Bytebuffer* code)
{
Symbol* basetype = var->typ.basetype;
nc_type typecode = basetype->typ.typecode;
switch (typecode) {
case NC_BYTE:
case NC_SHORT:
case NC_INT:
case NC_FLOAT:
case NC_DOUBLE:
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_var_%s(%s, %s, (%s)",
indented(1),
ncstype(basetype->typ.typecode),
jgroupncid(var->container),
jvarncid(var),
jtype(basetype->typ.typecode));
jpartial(stmt);
jprint(code);
jline("));");
jflush();
break;
case NC_CHAR:
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_var_%s(%s, %s, ",
indented(1),
ncstype(basetype->typ.typecode),
jgroupncid(var->container),
jvarncid(var));
jpartial(stmt);
jprint(code);
jline("));");
jflush();
break;
#ifdef USING_NETCDF4
/* !usingclassic only (except NC_STRING) */
case NC_UBYTE:
case NC_USHORT:
case NC_UINT:
case NC_INT64:
case NC_UINT64:
case NC_STRING:
if(usingclassic) {
verror("Non-classic type: %s",njtypename(basetype->typ.typecode));
return;
}
nprintf(stmt,sizeof(stmt),"%scheck_err(nc_put_vara_%s(%s, %s, new long[]{0}, new long[]{1}, ",
indented(1),
ncstype(basetype->typ.typecode),
jgroupncid(var->container),
jvarncid(var));
jpartial(stmt);
jprint(code);
jline("));");
jflush();
break;
#endif
default: break;
}
}
/*
* Output a statement
*/
void
jprint(Bytebuffer* buf)
{
bbAppendn(jcode,bbContents(buf),bbLength(buf));
}
void
jpartial(char* line)
{
bbCat(jcode,line);
}
void
jline(char* line)
{
jpartial(line);
bbCat(jcode,"\n");
}
void
jlined(int n, char* line)
{
bbCat(jcode,indented(n));
jline(line);
}
void
jflush(void)
{
if(bbLength(jcode) > 0) {
bbAppend(jcode,'\0');
fputs(bbContents(jcode),stdout);
fflush(stdout);
bbClear(jcode);
}
}
/* Result is a pool string or a constant => do not free*/
char*
jconst(Constant* ci)
{
char tmp[64];
tmp[0] = '\0';
switch (ci->nctype) {
case NC_CHAR:
{
strcpy(tmp,"'");
escapifychar(ci->value.charv,tmp+1,'\'');
strcat(tmp,"'");
}
break;
case NC_BYTE:
sprintf(tmp,"%hhd",ci->value.int8v);
break;
case NC_SHORT:
sprintf(tmp,"%hd",ci->value.int16v);
break;
case NC_INT:
sprintf(tmp,"%d",ci->value.int32v);
break;
case NC_FLOAT:
sprintf(tmp,"%.8g",ci->value.floatv);
break;
case NC_DOUBLE:
sprintf(tmp,"%.16g",ci->value.doublev);
break;
case NC_UBYTE:
sprintf(tmp,"%hhu",ci->value.uint8v);
break;
case NC_USHORT:
sprintf(tmp,"%hu",ci->value.uint16v);
break;
case NC_UINT:
sprintf(tmp,"%uL",ci->value.uint32v); /* upgrade to long */
break;
case NC_INT64:
sprintf(tmp,"%lldL",ci->value.int64v);
break;
case NC_UINT64: {
/* HACK to handle unsigned values */
long long l = (long)ci->value.uint64v;
sprintf(tmp,"%lldL",l);
} break;
case NC_ECONST:
sprintf(tmp,"%s",jname(ci->value.enumv));
break;
case NC_STRING:
{
char* escaped = escapify(ci->value.stringv.stringv,
'"',ci->value.stringv.len);
char* result = poolalloc(1+2+strlen(escaped));
strcpy(result,"\"");
strcat(result,escaped);
strcat(result,"\"");
return result;
}
break;
case NC_OPAQUE: {
char* bstring;
char* p;
int bslen;
bslen=(4*ci->value.opaquev.len);
bstring = poolalloc(bslen+2+1);
strcpy(bstring,"\"");
p = ci->value.opaquev.stringv;
while(*p) {
strlcat(bstring,"\\x",bslen+3);
strlcat(bstring,p,bslen+3);
p += 2;
}
strlcat(bstring,"\"",bslen+3);
return bstring;
} break;
default: PANIC1("ncstype: bad type code: %d",ci->nctype);
}
return pooldup(tmp); /*except for NC_STRING and NC_OPAQUE*/
}
void
jquotestring(Bytebuffer* databuf, char quote)
{
char* escaped = jescapify(bbContents(databuf),'"',bbLength(databuf));
bbClear(databuf);
bbAppend(databuf,quote);
if(escaped != NULL) bbCat(databuf,escaped);
bbAppend(databuf,quote);
}
/* Compute the name for a given symbol*/
/* Cache in symbol->lname*/
const char*
jname(Symbol* sym)
{
if(sym->lname == NULL) {
#ifdef USE_NETCDF4
if(sym->subclass == NC_FIELD || sym->subclass == NC_ECONST) {
sym->lname = nulldup(jcodify(sym->name));
} else
#endif
if(sym->objectclass == NC_ATT && sym->att.var != NULL) {
/* Attribute name must be prefixed with the cname of the*/
/* associated variable*/
char* lname;
lname = (char*)ecalloc(strlen(sym->att.var->name)
+strlen(sym->name)
+1+1);
lname[0] = '\0';
strcpy(lname,sym->att.var->name);
strcat(lname,"_");
strcat(lname,sym->name);
/* Now convert to java acceptable name */
sym->lname = nulldup(jcodify(lname));
} else {
/* convert to language form*/
#ifdef USE_NETCDF4
sym->lname = nulldup(jcodify(jprefixed(sym->prefix,sym->name,"_")));
#else
sym->lname = nulldup(jcodify(sym->name)); /* convert to usable form*/
#endif
}
}
return sym->lname;
}
#ifdef USE_NETCDF4
/* Result is pool alloc'd*/
static char*
jprefixed(List* prefix, char* suffix, char* separator)
{
int slen;
int plen;
int i;
char* result;
ASSERT(suffix != NULL);
plen = prefixlen(prefix);
if(prefix == NULL || plen == 0) return pooldup(suffix);
/* plen > 0*/
slen = 0;
for(i=0;i<plen;i++) {
Symbol* sym = (Symbol*)listget(prefix,i);
slen += (strlen(sym->name)+strlen(separator));
}
slen += strlen(suffix);
slen++; /* for null terminator*/
result = poolalloc(slen);
result[0] = '\0';
/* Leave off the root*/
i = (rootgroup == (Symbol*)listget(prefix,0))?1:0;
for(;i<plen;i++) {
Symbol* sym = (Symbol*)listget(prefix,i);
strcat(result,sym->name); /* append "<prefix[i]/>"*/
strcat(result,separator);
}
strcat(result,suffix); /* append "<suffix>"*/
return result;
}
#endif /*USE_NETCDF4*/
#endif /*ENABLE_JAVA*/