[MAJOR: implementation of 'database' code generator.
nickel@x9c.fr**20080920164745] {
hunk ./build.xml 70
+
hunk ./build.xml 115
- includes="**/*.class"/>
+ includes="**/*.class,**/*.clazz"/>
addfile ./resources/dtds/dbmodule.dtd
hunk ./resources/dtds/dbmodule.dtd 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
addfile ./src/fr/x9c/nickel/database/CodeGenerator.java
hunk ./src/fr/x9c/nickel/database/CodeGenerator.java 1
+/*
+ * This file is part of Nickel.
+ * Copyright (C) 2007-2008 Xavier Clerc.
+ *
+ * Nickel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Nickel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package fr.x9c.nickel.database;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.sql.ParameterMetaData;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import fr.x9c.nickel.NickelException;
+import fr.x9c.nickel.Parameters;
+import fr.x9c.nickel.GenerateCCode;
+
+/**
+ * This class implements the actual code generation.
+ * This class contains the code generating OCaml source, generation of Java and
+ * C sources are done by using methods from {@link fr.x9c.nickel.database.GenerateJavaCode}
+ * and {@link fr.x9c.nickel.GenerateCCode}.
+ *
+ * @author Xavier Clerc
+ * @version 1.1
+ * @since 1.1
+ */
+final class CodeGenerator {
+
+ /** Message printed when code generation starts, if verbose. */
+ private static final String CODE_GEN_TRACE = "code generation ...";
+
+ /** Exception message for an i/o error. */
+ private static final String EXCEPTION_IO =
+ "an i/o error occurred while writing files";
+
+ /** Exception message for a database error. */
+ private static final String DATABASE_ERROR =
+ "a database error occurred while generating bindings";
+
+ /** Tabulation for OCaml sources. */
+ private static final String OCAML_TABS = " ";
+
+ /** Tabulation for Java and C sources. */
+ private static final String TABS = " ";
+
+ /** Parameters controlling code generation. */
+ private final Parameters params;
+
+ /** Name of generated module. */
+ private final String module;
+
+ /** Name of generated module (uncapitalized). */
+ private final String uncapModule;
+
+ /**
+ * List of statements to generate code for.
+ */
+ private final List statements;
+
+ /** Data of OCaml source, as bytes. */
+ private final ByteArrayOutputStream mlSource;
+
+ /** Data of Java source, as bytes. */
+ private final ByteArrayOutputStream javaSource;
+
+ /** Data of C source, as bytes. */
+ private final ByteArrayOutputStream cSource;
+
+ /** Output stream for OCaml source. */
+ private final PrintStream ml;
+
+ /** Output stream for Java source. */
+ private final PrintStream java;
+
+ /** Output stream for C source. */
+ private final PrintStream c;
+
+ /**
+ * Where comments should be printed during process
+ * (null if such print should not occur).
+ */
+ private final PrintStream verbose;
+
+ /**
+ * Where debug elements (e.g. stack traces) should be printed when
+ * an error occurs (null if such print should not occur).
+ */
+ private final PrintStream debug;
+
+ /**
+ * Constructs a code generator.
+ * @param p parameters for code generation - should not be null
+ * @param mdl name of generated module - should not be null
+ * @param stats list of statements to generate primitives for
+ * - should not be null
+ * passed class instances should also be resolved
+ * @throws NickelException if an error occurs
+ */
+ CodeGenerator(final Parameters p,
+ final String mdl,
+ final List stats)
+ throws NickelException {
+ assert p != null : "null p";
+ assert mdl != null : "null mdl";
+ assert stats != null : "null stats";
+ this.params = p;
+ this.module = mdl;
+ this.uncapModule = Character.toLowerCase(this.module.charAt(0))
+ + this.module.substring(1);
+ this.statements = new ArrayList(stats);
+
+ this.mlSource = new ByteArrayOutputStream();
+ this.javaSource = new ByteArrayOutputStream();
+ this.cSource = new ByteArrayOutputStream();
+ this.ml = new PrintStream(this.mlSource);
+ this.java = new PrintStream(this.javaSource);
+ this.c = new PrintStream(this.cSource);
+ this.verbose = this.params.getVerboseStream();
+ this.debug = this.params.getDebugStream();
+ } // end constructor(Parameters, String, List)
+
+ /**
+ * Actually generate the code, according to the elements passed to the
+ * constructor.
+ * @throws NickelException if any error occurs
+ */
+ void generate() throws NickelException {
+ if (this.verbose != null) {
+ this.verbose.println(CodeGenerator.CODE_GEN_TRACE);
+ } // end if
+
+ // headers
+ final String now = (new Date()).toString();
+ GenerateCCode.forHeader(this.c, now);
+ this.ml.printf("(* Nickel-generated source file -- %s *)\n", now);
+ this.ml.printf("\n");
+ GenerateJavaCode.forHeader(this.java,
+ this.module,
+ this.params.getJavaPackage(),
+ now,
+ CodeGenerator.TABS);
+
+ // common methods
+ final String connectFunction = String.format("%scommon_%s_connect",
+ this.params.getPrimitivePrefix(),
+ this.uncapModule);
+ final String isConnectedFunction = String.format("%scommon_%s_is_connected",
+ this.params.getPrimitivePrefix(),
+ this.uncapModule);
+ final String disconnectFunction = String.format("%scommon_%s_disconnect",
+ this.params.getPrimitivePrefix(),
+ this.uncapModule);
+ GenerateJavaCode.forCommonMethods(this.java,
+ this.module,
+ connectFunction,
+ isConnectedFunction,
+ disconnectFunction,
+ this.statements,
+ CodeGenerator.TABS);
+ this.ml.printf("external connect : string -> string option -> string option -> unit = \"%s\"\n\n",
+ connectFunction);
+ GenerateCCode.forPrimitive(this.c, connectFunction, 3, CodeGenerator.TABS);
+
+ this.ml.printf("external is_connected : unit -> bool = \"%s\"\n\n",
+ isConnectedFunction);
+ GenerateCCode.forPrimitive(this.c, isConnectedFunction, 1, CodeGenerator.TABS);
+
+ this.ml.printf("external disconnect : unit -> unit = \"%s\"\n\n",
+ disconnectFunction);
+ GenerateCCode.forPrimitive(this.c, disconnectFunction, 1, CodeGenerator.TABS);
+
+ // code generation
+ int index = 0;
+ try {
+ for (SQLStatement st : this.statements) {
+ generateStatement(st);
+ GenerateJavaCode.forStatement(this.java,
+ String.format("%sexecute_%s_",
+ this.params.getPrimitivePrefix(),
+ this.uncapModule),
+ index,
+ st,
+ CodeGenerator.TABS);
+ if (st.isPrepared()) {
+ index++;
+ } // end if
+ } /// end for
+ } catch (final SQLException se) {
+ if (debug != null) {
+ se.printStackTrace(debug);
+ } // end if
+ throw new NickelException(NickelException.Phase.CODE_GENERATION,
+ CodeGenerator.DATABASE_ERROR);
+ } // end try/catch
+
+ // footers
+ GenerateJavaCode.forUtilityMethods(this.java, this.module, CodeGenerator.TABS);
+ GenerateJavaCode.forFooter(this.java);
+ GenerateCCode.forFooter(this.c);
+
+ // file save
+ writeFiles();
+ } // end method 'generate()'
+
+ /**
+ * Actually generate the code for a given statement,
+ * according to the elements passed to the constructor.
+ * @param statement statement to generate code for - should not be null
+ * @throws SQLException if a database error occurs
+ * @throws NickelException if any error occurs
+ */
+ private void generateStatement(final SQLStatement statement)
+ throws SQLException, NickelException {
+ assert statement != null : "null statement";
+ switch (statement.getKind()) {
+ case COMMAND:
+ final String commandFunction = String.format("%sexecute_%s_%s",
+ this.params.getPrimitivePrefix(),
+ this.uncapModule,
+ statement.getName());
+ this.ml.printf("external %s : CadmiumJDBC.Connection.t%s%s -> int32 = %s\"%s\"\n\n",
+ statement.getName(),
+ statement.isPrepared() ? " option" : "",
+ generateSignature(statement.getParameterMetaData()),
+ 1 + statement.getParameterMetaData().getParameterCount() > 5
+ ? ("\"" + commandFunction + "_bytecode\" ")
+ : "",
+ commandFunction);
+ GenerateCCode.forPrimitive(this.c, commandFunction, 1 + statement.getParameterMetaData().getParameterCount(), CodeGenerator.TABS);
+ if (1 + statement.getParameterMetaData().getParameterCount() > 5) {
+ final String synonymName = commandFunction + "_bytecode";
+ GenerateCCode.forSynonym(this.c,
+ commandFunction,
+ synonymName,
+ 1 + statement.getParameterMetaData().getParameterCount(),
+ CodeGenerator.TABS);
+ } // end if
+ break;
+ case QUERY:
+ final String function = String.format("%sexecute_%s_%s",
+ this.params.getPrimitivePrefix(),
+ this.uncapModule,
+ statement.getName());
+ final StringBuilder parametersBuilder = new StringBuilder();
+ final int lenParams = statement.getParameterMetaData().getParameterCount();
+ for (int i = 1; i <= lenParams; i++) {
+ parametersBuilder.append(" p");
+ parametersBuilder.append(i);
+ } // end for
+ final String parameters = parametersBuilder.toString();
+ this.ml.printf("external %s : CadmiumJDBC.Connection.t%s%s -> CadmiumJDBC.ResultSet.t = %s\"%s\"\n\n",
+ statement.getName(),
+ statement.isPrepared() ? " option" : "",
+ generateSignature(statement.getParameterMetaData()),
+ 1 + statement.getParameterMetaData().getParameterCount() > 5
+ ? ("\"" + function + "_bytecode\" ")
+ : "",
+ function);
+ GenerateCCode.forPrimitive(this.c, function, 1 + statement.getParameterMetaData().getParameterCount(), CodeGenerator.TABS);
+ if (1 + statement.getParameterMetaData().getParameterCount() > 5) {
+ final String synonymName = function + "_bytecode";
+ GenerateCCode.forSynonym(this.c,
+ function,
+ synonymName,
+ 1 + statement.getParameterMetaData().getParameterCount(),
+ CodeGenerator.TABS);
+ } // end if
+ this.ml.printf("class %s conn%s =\n", statement.getName(), parameters);
+ this.ml.printf("%sobject\n", OCAML_TABS);
+ this.ml.printf("%s%sval rs = %s conn%s\n", OCAML_TABS, OCAML_TABS, statement.getName(), parameters);
+ this.ml.printf("%s%smethod close = CadmiumJDBC.ResultSet.close rs\n",
+ OCAML_TABS, OCAML_TABS);
+ this.ml.printf("%s%smethod get_result_set = rs\n",
+ OCAML_TABS, OCAML_TABS);
+ this.ml.printf("%s%smethod next =\n",
+ OCAML_TABS, OCAML_TABS);
+ final ResultSetMetaData meta = statement.getResultSetMetaData();
+ final int len = meta.getColumnCount();
+ boolean existsNotNull = false;
+ for (int i = 1; i <= len; i++) {
+ existsNotNull = existsNotNull
+ || (meta.isNullable(i) == ResultSetMetaData.columnNoNulls);
+ } // end for
+ if (existsNotNull) {
+ this.ml.printf("%s%s%slet notnull = function Some x -> x | None -> failwith (\"Unexpected null value\") in\n",
+ OCAML_TABS, OCAML_TABS, OCAML_TABS);
+ } // end if
+ this.ml.printf("%s%s%slet res = try CadmiumJDBC.ResultSet.next rs with _ -> false in\n",
+ OCAML_TABS, OCAML_TABS, OCAML_TABS);
+ this.ml.printf("%s%s%sif res then\n",
+ OCAML_TABS, OCAML_TABS, OCAML_TABS);
+ for (int i = 1; i <= len; i++) {
+ final boolean notnull =
+ meta.isNullable(i) == ResultSetMetaData.columnNoNulls;
+ this.ml.printf("%s%s%s%s%s(CadmiumJDBC.ResultSet.get_%s_idx rs %d)%s%s\n",
+ OCAML_TABS, OCAML_TABS, OCAML_TABS, OCAML_TABS,
+ notnull ? "(notnull " : "",
+ getAccessor(meta.getColumnType(i)), i,
+ notnull ? ")" : "",
+ i < len ? "," : "");
+ } // end for
+ this.ml.printf("%s%s%selse\n",
+ OCAML_TABS, OCAML_TABS, OCAML_TABS);
+ this.ml.printf("%s%s%s%sraise Not_found\n",
+ OCAML_TABS, OCAML_TABS, OCAML_TABS, OCAML_TABS);
+ if (statement.isUpdatable()) {
+ for (int i = 1; i <= len; i++) {
+ final boolean notnull =
+ meta.isNullable(i) == ResultSetMetaData.columnNoNulls;
+ this.ml.printf("%s%smethod update_%d x =\n",
+ OCAML_TABS, OCAML_TABS, i);
+ this.ml.printf("%s%s%s%sCadmiumJDBC.ResultSet.update_%s_idx rs %d %sx%s;\n",
+ OCAML_TABS, OCAML_TABS, OCAML_TABS, OCAML_TABS,
+ getAccessor(meta.getColumnType(i)),
+ i,
+ notnull ? "(Some " : "",
+ notnull ? ")" : "");
+ this.ml.printf("%s%s%s%sCadmiumJDBC.ResultSet.update_row rs\n",
+ OCAML_TABS, OCAML_TABS, OCAML_TABS, OCAML_TABS);
+ } // end for
+ } // end if
+ this.ml.printf("%send\n\n", OCAML_TABS);
+ break;
+ default:
+ // should not happen
+ assert false : "invalid case";
+ } // end switch
+ } // end method 'generate(SQLStatement)'
+
+ /**
+ * Actually writes the files and closes them.
+ * @throws NickelException if an i/o error occurs
+ */
+ private void writeFiles() throws NickelException {
+ final IOException ioe1 =
+ writeFile(new File(this.params.getOCamlDirectory(),
+ this.uncapModule + ".ml"),
+ this.mlSource,
+ this.ml,
+ this.debug);
+ final IOException ioe2 =
+ writeFile(new File(this.params.getJavaDirectory(),
+ this.module + ".java"),
+ this.javaSource,
+ this.java,
+ this.debug);
+ final IOException ioe3 =
+ writeFile(new File(this.params.getCDirectory(),
+ this.uncapModule + ".c"),
+ this.cSource,
+ this.c,
+ this.debug);
+ if ((ioe1 != null) || (ioe2 != null) || (ioe3 != null)) {
+ throw new NickelException(NickelException.Phase.CODE_GENERATION,
+ CodeGenerator.EXCEPTION_IO);
+ } // end if
+ } // end method 'writeFiles()'
+
+ /**
+ * Flushes a PrintStream, writes a ByteArrayOutputStream
+ * to a file and then flushes and closes the file.
+ * @param file file to write data to - should not be null
+ * @param byteStream stream to write to file - should not be null
+ * @param printStream stream to flush before file writing
+ * - should not be null
+ * @param debug where debug elements (e.g. stack traces)
+ * should be printed when an error occurs
+ * (null if such print should not occur)
+ * @return the exception raise if any, null otherwise
+ */
+ private static IOException writeFile(final File file,
+ final ByteArrayOutputStream byteStream,
+ final PrintStream printStream,
+ final PrintStream debug) {
+ assert file != null : "null file";
+ assert byteStream != null : "null byteStream";
+ assert printStream != null : "null printStream";
+ FileOutputStream f = null;
+ try {
+ printStream.flush();
+ f = new FileOutputStream(file);
+ f.write(byteStream.toByteArray());
+ return null;
+ } catch (final IOException ioe) {
+ if (debug != null) {
+ ioe.printStackTrace(debug);
+ } // end if
+ return ioe;
+ } finally {
+ if (f != null) {
+ try {
+ f.close();
+ } catch (final IOException ioe) {
+ // nothing to do on simple cleanup
+ } // end try/catch
+ } // end if
+ } // end try/catch/finally
+ } // end method 'writeFile(File, ByteArrayOutputStream, PrintStream, PrintStream)'
+
+ /**
+ * Returns the accessor name for a JDBC type.
+ * @param type JDBC type
+ * @return the accessor name for a JDBC type
+ */
+ private static String getAccessor(final int type) {
+ switch (type) {
+ case Types.ARRAY:
+ return "object";
+ case Types.BIGINT:
+ return "long";
+ case Types.BINARY:
+ return "bytes";
+ case Types.BIT:
+ return "boolean";
+ case Types.BLOB:
+ return "blob";
+ case Types.BOOLEAN:
+ return "boolean";
+ case Types.CHAR:
+ return "string";
+ case Types.CLOB:
+ return "clob";
+ case Types.DATALINK:
+ return "string";
+ case Types.DATE:
+ return "date";
+ case Types.DECIMAL:
+ return "big_decimal";
+ case Types.DISTINCT:
+ return "object";
+ case Types.DOUBLE:
+ return "double";
+ case Types.FLOAT:
+ return "float";
+ case Types.INTEGER:
+ return "int";
+ case Types.JAVA_OBJECT:
+ return "object";
+ case Types.LONGNVARCHAR:
+ return "string";
+ case Types.LONGVARBINARY:
+ return "bytes";
+ case Types.LONGVARCHAR:
+ return "string";
+ case Types.NCHAR:
+ return "string";
+ case Types.NCLOB:
+ return "nclob";
+ case Types.NUMERIC:
+ return "big_decimal";
+ case Types.NVARCHAR:
+ return "string";
+ case Types.OTHER:
+ return "object";
+ case Types.REAL:
+ return "double";
+ case Types.REF:
+ return "ref";
+ case Types.ROWID:
+ return "row_id";
+ case Types.SMALLINT:
+ return "short";
+ case Types.SQLXML:
+ return "sqlxml";
+ case Types.STRUCT:
+ return "object";
+ case Types.TIME:
+ return "time";
+ case Types.TIMESTAMP:
+ return "timestamp";
+ case Types.TINYINT:
+ return "byte";
+ case Types.VARBINARY:
+ return "bytes";
+ case Types.VARCHAR:
+ return "string";
+ default:
+ // should not happen
+ assert false : "invalid case";
+ return "";
+ } // end switch
+ } // end method 'getAccessor(int)'
+
+ /**
+ * Generates the OCaml signature from parameters.
+ * @param params parameters to generate signature from - should not be null
+ * @return the OCaml signature corresponding to the passed parameters
+ * @throws SQLException if a database error occurs
+ */
+ private static String generateSignature(final ParameterMetaData params)
+ throws SQLException {
+ assert params != null : "null params";
+ final int len = params.getParameterCount();
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 1; i <= len; i++) {
+ sb.append(" -> ");
+ switch (params.getParameterType(i)) {
+ case Types.ARRAY:
+ sb.append("Cadmium.java_object");
+ break;
+ case Types.BIGINT:
+ sb.append("int64");
+ break;
+ case Types.BINARY:
+ sb.append("string");
+ break;
+ case Types.BIT:
+ sb.append("boolean");
+ break;
+ case Types.BLOB:
+ sb.append("CadmiumJDBC.Blob.t");
+ break;
+ case Types.BOOLEAN:
+ sb.append("boolean");
+ break;
+ case Types.CHAR:
+ sb.append("string");
+ break;
+ case Types.CLOB:
+ sb.append("CadmiumJDBC.Clob.t");
+ break;
+ case Types.DATALINK:
+ sb.append("string");
+ break;
+ case Types.DATE:
+ sb.append("CadmiumJDBC.Date.t");
+ break;
+ case Types.DECIMAL:
+ sb.append("CadmiumMath.BigDecimal.t");
+ break;
+ case Types.DISTINCT:
+ sb.append("Cadmium.java_object");
+ break;
+ case Types.DOUBLE:
+ sb.append("float");
+ break;
+ case Types.FLOAT:
+ sb.append("float");
+ break;
+ case Types.INTEGER:
+ sb.append("int32");
+ break;
+ case Types.JAVA_OBJECT:
+ sb.append("Cadmium.java_object");
+ break;
+ case Types.LONGNVARCHAR:
+ sb.append("string");
+ break;
+ case Types.LONGVARBINARY:
+ sb.append("string");
+ break;
+ case Types.LONGVARCHAR:
+ sb.append("string");
+ break;
+ case Types.NCHAR:
+ sb.append("string");
+ break;
+ case Types.NCLOB:
+ sb.append("CadmiumJDBC.NClob.t");
+ break;
+ case Types.NUMERIC:
+ sb.append("CadmiumMath.BigDecimal.t");
+ break;
+ case Types.NVARCHAR:
+ sb.append("string");
+ break;
+ case Types.OTHER:
+ sb.append("Cadmium.java_object");
+ break;
+ case Types.REAL:
+ sb.append("float");
+ break;
+ case Types.REF:
+ sb.append("CadmiumJDBC.Ref.t");
+ break;
+ case Types.ROWID:
+ sb.append("CadmiumJDBC.RowId.t");
+ break;
+ case Types.SMALLINT:
+ sb.append("int");
+ break;
+ case Types.SQLXML:
+ sb.append("CadmiumJDBC.SQLXML.t");
+ break;
+ case Types.STRUCT:
+ sb.append("Cadmium.java_object");
+ break;
+ case Types.TIME:
+ sb.append("CadmiumJDBC.Time.t");
+ break;
+ case Types.TIMESTAMP:
+ sb.append("CadmiumJDBC.Timestamp.t");
+ break;
+ case Types.TINYINT:
+ sb.append("int");
+ break;
+ case Types.VARBINARY:
+ sb.append("string");
+ break;
+ case Types.VARCHAR:
+ sb.append("string");
+ break;
+ default:
+ // should not happen
+ assert false : "invalid case";
+ } // end switch
+ if (params.isNullable(i) != ParameterMetaData.parameterNoNulls) {
+ sb.append(" option");
+ } // end if
+ } // end for
+ return sb.toString();
+ } // end method 'generateSignature(ParameterMetaData)'
+
+} // end class 'CodeGenerator'
addfile ./src/fr/x9c/nickel/database/Connector.java
hunk ./src/fr/x9c/nickel/database/Connector.java 1
+/*
+ * This file is part of Nickel.
+ * Copyright (C) 2007-2008 Xavier Clerc.
+ *
+ * Nickel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Nickel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package fr.x9c.nickel.database;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+/**
+ * This class implements a class with a method connecting to a database.
+ * It is used to circumvent the fact that the caller of DriverManager.getConnection(-)
+ * should be in the same class loader than the driver it wants to use for connection.
+ * This class will hence be dynamically loaded by the same class loader that was used to
+ * load the driver. Then reflection will be used to create an instance of this class allowing
+ * to use connect as a synonym for DriverManager.getConnection(-),
+ * living in the appropriate class loader.
+ *
+ * @author Xavier Clerc
+ * @version 1.1
+ * @since 1.1
+ */
+public final class Connector {
+
+ /**
+ * Empty constructor.
+ */
+ public Connector() {
+ } // end empty constructor
+
+ /**
+ * Connects to a database.
+ * Bare synonym for {@link DriverManager.getConnection(java.lang.String, java.lang.String, java.lang.String)}.
+ * @param url database URL - should not be null
+ * @param user database user
+ * @param password database password
+ * @return a connection to the database
+ */
+ public Connection connect(final String url, final String user, final String password)
+ throws SQLException {
+ assert url != null : "null url";
+ return DriverManager.getConnection(url, user, password);
+ } // end method 'connect(String, String, String)'
+
+} // end class 'Connector'
hunk ./src/fr/x9c/nickel/database/DatabaseModuleProducer.java 23
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.LinkedList;
+import java.util.List;
hunk ./src/fr/x9c/nickel/database/DatabaseModuleProducer.java 41
+ /** Message for invalid key. */
+ private static final String INVALID_KEY =
+ "meta '%s' is not properly defined";
+
+ /** Message for invalid driver. */
+ private static final String UNABLE_TO_LOAD_DRIVER =
+ "unable to load driver '%s'";
+
+ /** Message for connection error. */
+ private static final String UNABLE_TO_CONNECT =
+ "unable to connect to database";
+
+ /** Message for close error. */
+ private static final String UNABLE_TO_CLOSE =
+ "unable to close database";
+
+ /** The key of the 'driver' meta. */
+ private static final String META_DRIVER = "DRIVER";
+
+ /** The key of the 'url' meta. */
+ private static final String META_URL = "URL";
+
+ /** The key of the 'user' meta. */
+ private static final String META_USER = "USER";
+
+ /** The key of the 'password' meta. */
+ private static final String META_PASSWORD = "PASSWORD";
+
+ /** Connection to database.*/
+ private Connection connection;
+
+ /** List of statements to process. */
+ private final List statements;
+
+ /** Name of module to generate. */
+ private String module;
+
hunk ./src/fr/x9c/nickel/database/DatabaseModuleProducer.java 82
+ this.connection = null;
+ this.statements = new LinkedList();
+ this.module = null;
hunk ./src/fr/x9c/nickel/database/DatabaseModuleProducer.java 93
+ this.module = ParseXML.parse(is, this.statements, this.meta, verbose, debug);
hunk ./src/fr/x9c/nickel/database/DatabaseModuleProducer.java 102
+ final List drivers = getMeta(DatabaseModuleProducer.META_DRIVER);
+ if (drivers != null) {
+ for (String d : drivers) {
+ try {
+ Class.forName(d, true, cl);
+ } catch (final ClassNotFoundException cnfe) {
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ String.format(DatabaseModuleProducer.UNABLE_TO_LOAD_DRIVER, d));
+ } // end try/catch
+ } // end for
+ } // end if
+ try {
+ final String url = getMetaValue(DatabaseModuleProducer.META_URL, false);
+ final String user = getMetaValue(DatabaseModuleProducer.META_USER, true);
+ final String password = getMetaValue(DatabaseModuleProducer.META_PASSWORD, true);
+ final Class> connector = cl.loadClass("fr.x9c.nickel.database.Connector");
+ this.connection =
+ (Connection) connector.getMethod("connect", String.class, String.class, String.class).invoke(connector.newInstance(), url, user, password);
+ if (this.connection == null) {
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ String.format(DatabaseModuleProducer.UNABLE_TO_CONNECT));
+ } // end if
+ } catch (final ClassNotFoundException cnfe) {
+ if (debug != null) {
+ cnfe.printStackTrace(debug);
+ } // end if
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ String.format(DatabaseModuleProducer.UNABLE_TO_CONNECT));
+ } catch (final NoSuchMethodException nsme) {
+ if (debug != null) {
+ nsme.printStackTrace(debug);
+ } // end if
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ String.format(DatabaseModuleProducer.UNABLE_TO_CONNECT));
+ } catch (final InstantiationException ie) {
+ if (debug != null) {
+ ie.printStackTrace(debug);
+ } // end if
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ String.format(DatabaseModuleProducer.UNABLE_TO_CONNECT));
+ } catch (final IllegalAccessException iae) {
+ if (debug != null) {
+ iae.printStackTrace(debug);
+ } // end if
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ String.format(DatabaseModuleProducer.UNABLE_TO_CONNECT));
+ } catch (final java.lang.reflect.InvocationTargetException ite) {
+ if (debug != null) {
+ ite.printStackTrace(debug);
+ } // end if
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ ite.getCause().getMessage());
+ } // end try/catch
+ for (SQLStatement s : this.statements) {
+ s.resolve(this.connection, verbose, debug);
+ } // end for
hunk ./src/fr/x9c/nickel/database/DatabaseModuleProducer.java 166
+ try {
+ final CodeGenerator gen =
+ new CodeGenerator(params, this.module, this.statements);
+ gen.generate();
+ } finally {
+ try {
+ this.connection.close();
+ } catch (final SQLException se) {
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ String.format(DatabaseModuleProducer.UNABLE_TO_CLOSE));
+ } // end try/catch
+ } // end try/finally
hunk ./src/fr/x9c/nickel/database/DatabaseModuleProducer.java 180
+ /**
+ * Returns a meta value for a given key.
+ * @param key meta key - should not be null
+ * @param returnsNull whether null may be returned if the meta is no set
+ * @return the meta for the passed key
+ * @throws NickelException if the meta has multiple definition,
+ * or no definition and returnsNull is false
+ */
+ private String getMetaValue(final String key, final boolean returnsNull)
+ throws NickelException {
+ assert key != null : "null key";
+ final List l = this.meta.get(key);
+ if (l != null) {
+ if (l.size() == 1) {
+ return l.get(0);
+ } else if ((l.size() == 0) && returnsNull) {
+ return null;
+ } else {
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ String.format(DatabaseModuleProducer.INVALID_KEY, key));
+ } // end if/else
+ } else {
+ if (returnsNull) {
+ return null;
+ } else {
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ String.format(DatabaseModuleProducer.INVALID_KEY, key));
+ } // end if/else
+ } // end if/else
+ } // end method 'getMetaValue(String, boolean)'
+
addfile ./src/fr/x9c/nickel/database/GenerateJavaCode.java
hunk ./src/fr/x9c/nickel/database/GenerateJavaCode.java 1
+/*
+ * This file is part of Nickel.
+ * Copyright (C) 2007-2008 Xavier Clerc.
+ *
+ * Nickel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Nickel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package fr.x9c.nickel.database;
+
+import java.io.PrintStream;
+import java.sql.ParameterMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.List;
+
+/**
+ * This class provides utility methods for Java code generation.
+ *
+ * @author Xavier Clerc
+ * @version 1.1
+ * @since 1.0
+ */
+final class GenerateJavaCode {
+
+ /**
+ * No instance of this class.
+ */
+ private GenerateJavaCode() {
+ } // end empty constructor
+
+ /**
+ * Generates the Java code for a primitive provider header.
+ * @param out where to print generated code - should not be null
+ * @param className class name of primitive provider - should not be null
+ * @param packageName package name of primitive provider - should not be null
+ * @param timestamp creation date, as a string - should not be null
+ * @param tabs tabulation value - should not be null
+ */
+ static void forHeader(final PrintStream out,
+ final String className,
+ final String packageName,
+ final String timestamp,
+ final String tabs) {
+ assert out != null : "null out";
+ assert className != null : "null className";
+ assert packageName != null : "null packageName";
+ assert timestamp != null : "null timestamp";
+ assert tabs != null : "null tabs";
+
+ out.printf("/* Nickel-generated source file -- %s */\n", timestamp);
+ out.printf("package %s;\n", packageName);
+ out.printf("\n");
+ out.printf("import java.sql.Connection;\n");
+ out.printf("import java.sql.DriverManager;\n");
+ out.printf("import java.sql.PreparedStatement;\n");
+ out.printf("import java.sql.ResultSet;\n");
+ out.printf("import java.sql.SQLException;\n");
+ out.printf("\n");
+ out.printf("import fr.x9c.cadmium.kernel.Block;\n");
+ out.printf("import fr.x9c.cadmium.kernel.CodeRunner;\n");
+ out.printf("import fr.x9c.cadmium.kernel.Custom;\n");
+ out.printf("import fr.x9c.cadmium.kernel.Fail;\n");
+ out.printf("import fr.x9c.cadmium.kernel.Primitive;\n");
+ out.printf("import fr.x9c.cadmium.kernel.PrimitiveProvider;\n");
+ out.printf("import fr.x9c.cadmium.kernel.Value;\n");
+ out.printf("import fr.x9c.cadmium.primitives.cadmium.Cadmium;\n");
+ out.printf("import fr.x9c.cadmium.primitives.cadmium.CustomObject;\n");
+ out.printf("import fr.x9c.cadmium.primitives.cadmiumjdbc.JDBCSlot;\n");
+ out.printf("\n");
+ out.printf("@PrimitiveProvider\n");
+ out.printf("public final class %s {\n", className);
+ out.printf("\n");
+ out.printf("%sprivate %s() {\n", tabs, className);
+ out.printf("%s}\n", tabs);
+ out.printf("\n");
+ } // end method 'forHeader(PrintStream, String, String, String)'
+
+ /**
+ * Generates the Java code for a list of statements, and common methods.
+ * @param out where to print generated code - should not be null
+ * @param module module name - should not be null
+ * @param connectName name of connect primitive - should not be null
+ * @param isConnectedName name of is_connected primitive - should not be null
+ * @param disconnectName name of disconnect primitive - should not be null
+ * @param statements statements to generate code for - should not be null
+ * @param tabs tabulation value - should not be null
+ */
+ static void forCommonMethods(final PrintStream out,
+ final String module,
+ final String connectName,
+ final String isConnectedName,
+ final String disconnectName,
+ final List statements,
+ final String tabs) {
+ assert out != null : "null out";
+ assert module != null : "null module";
+ assert connectName != null : "null connectName";
+ assert isConnectedName != null : "null isConnectedName";
+ assert disconnectName != null : "null disconnectName";
+ assert statements != null : "null statements";
+ assert tabs != null : "null tabs";
+
+ // connect
+ out.printf("%s@Primitive\n", tabs);
+ out.printf("%spublic static Value %s(final CodeRunner ctxt, final Value url, final Value user, final Value password) throws Fail.Exception {\n",
+ tabs, connectName);
+ out.printf("%s%s%s(ctxt, Value.UNIT);\n", tabs, tabs, disconnectName);
+ out.printf("%s%sfinal String urlString = url.asBlock().asString();\n", tabs, tabs);
+ out.printf("%s%sfinal String userString = user.isBlock() ? user.asBlock().get(0).asBlock().asString() : null;\n", tabs, tabs);
+ out.printf("%s%sfinal String passwordString = password.isBlock() ? password.asBlock().get(0).asBlock().asString() : null;\n", tabs, tabs);
+ out.printf("%s%stry {\n", tabs, tabs);
+ out.printf("%s%s%sfinal Connection conn = DriverManager.getConnection(urlString, userString, passwordString);\n", tabs, tabs, tabs);
+ out.printf("%s%s%sfinal PreparedStatement[] stats = {\n", tabs, tabs, tabs);
+ for (SQLStatement s : statements) {
+ if (s.isPrepared()) {
+ out.printf("%s%s%s%sconn.prepareStatement(\"%s\", ResultSet.TYPE_FORWARD_ONLY, %s),\n", tabs, tabs, tabs, tabs, s.getCode(), s.isUpdatable() ? "ResultSet.CONCUR_UPDATABLE" : "ResultSet.CONCUR_READ_ONLY");
+ } // end if
+ } // end for
+ out.printf("%s%s%s};\n", tabs, tabs, tabs);
+ out.printf("%s%s%sctxt.getContext().registerSlot(%s.class, new JDBCSlot(conn, stats));\n", tabs, tabs, tabs, module);
+ out.printf("%s%s%sreturn Value.UNIT;\n", tabs, tabs, tabs);
+ out.printf("%s%s} catch (final Exception e) {\n", tabs, tabs);
+ out.printf("%s%s%sCadmium.fail(ctxt, e);\n", tabs, tabs, tabs);
+ out.printf("%s%s%sreturn Value.UNIT;\n", tabs, tabs, tabs);
+ out.printf("%s%s}\n", tabs, tabs);
+ out.printf("%s}\n", tabs);
+ out.printf("\n");
+
+ // is_connected
+ out.printf("%s@Primitive\n", tabs);
+ out.printf("%spublic static Value %s(final CodeRunner ctxt, final Value unit) {\n",
+ tabs, isConnectedName);
+ out.printf("%s%sfinal JDBCSlot slot = getSlot(ctxt);\n", tabs, tabs);
+ out.printf("%s%stry {\n", tabs, tabs);
+ out.printf("%s%s%sreturn (slot != null) && !slot.getConnection().isClosed()\n", tabs, tabs, tabs);
+ out.printf("%s%s%s ? Value.TRUE\n", tabs, tabs, tabs);
+ out.printf("%s%s%s : Value.FALSE;\n", tabs, tabs, tabs);
+ out.printf("%s%s} catch (final Exception e) {\n", tabs, tabs);
+ out.printf("%s%s%sreturn Value.FALSE;\n", tabs, tabs, tabs);
+ out.printf("%s%s}\n", tabs, tabs);
+ out.printf("%s}\n", tabs);
+ out.printf("\n");
+
+ // disconnect
+ out.printf("%s@Primitive\n", tabs);
+ out.printf("%spublic static Value %s(final CodeRunner ctxt, final Value unit) {\n",
+ tabs, disconnectName);
+ out.printf("%s%sfinal JDBCSlot slot = getSlot(ctxt);\n", tabs, tabs);
+ out.printf("%s%stry {\n", tabs, tabs);
+ out.printf("%s%s%sif ((slot != null) && !slot.getConnection().isClosed()) {\n", tabs, tabs, tabs);
+ out.printf("%s%s%s%sfor (PreparedStatement ps : slot.getStatements()) {\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s%stry {\n", tabs, tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s%s%sps.close();\n", tabs, tabs, tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s%s} catch (final Exception e) {\n", tabs, tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s%s}\n", tabs, tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s}\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%stry {\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s%sslot.getConnection().close();\n", tabs, tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s} catch (final Exception e) {\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s}\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s}\n", tabs, tabs, tabs);
+ out.printf("%s%s%sreturn Value.UNIT;\n", tabs, tabs, tabs);
+ out.printf("%s%s} catch (final Exception e) {\n", tabs, tabs);
+ out.printf("%s%s%sreturn Value.UNIT;\n", tabs, tabs, tabs);
+ out.printf("%s%s}\n", tabs, tabs);
+ out.printf("%s}\n", tabs);
+ out.printf("\n");
+ } // end method 'forCommonMethods(PrintStream, String, String, String, String, List, String)'
+
+ /**
+ * Generates the Java code for a statements.
+ * @param out where to print generated code - should not be null
+ * @param prefix prefix to primitive name - should not be null
+ * @param index index of statement in list being generated
+ * @param stat statement to generate code for - should not be null
+ * @param tabs tabulation value - should not be null
+ */
+ static void forStatement(final PrintStream out,
+ final String prefix,
+ final int index,
+ final SQLStatement stat,
+ final String tabs)
+ throws SQLException {
+ assert out != null : "null out";
+ assert stat != null : "null stat";
+ assert tabs != null : "null tabs";
+ out.printf("%s@Primitive\n", tabs);
+ out.printf("%spublic static Value %s%s(final CodeRunner ctxt, final Value c",
+ tabs, prefix, stat.getName());
+ for (int i = 1; i <= stat.getParameterMetaData().getParameterCount(); i++) {
+ out.printf(", final Value p%d", i);
+ } // end for
+ out.printf(") throws Fail.Exception {\n");
+ if (stat.isPrepared()) {
+ out.printf("%s%sif (c.isBlock()) {\n", tabs, tabs);
+ out.printf("%s%s%sfinal Connection conn = (Connection) c.asBlock().asCustom();\n", tabs, tabs, tabs);
+ } else {
+ out.printf("%s%sfinal Connection conn = (Connection) c.asBlock().asCustom();\n", tabs, tabs);
+ out.printf("%s%sif (conn == null) {\n", tabs, tabs);
+ } // end if/else
+ if (stat.getKind() == SQLStatement.Kind.COMMAND) {
+ out.printf("%s%s%sfinal Block res = Block.createCustom(Custom.INT_32_SIZE, Custom.INT_32_OPS);\n", tabs, tabs, tabs);
+ } // end if
+ out.printf("%s%s%stry {\n", tabs, tabs, tabs);
+ out.printf("%s%s%s%sfinal PreparedStatement ps = conn.prepareStatement(\"%s\", ResultSet.TYPE_FORWARD_ONLY, %s);\n", tabs, tabs, tabs, tabs, stat.getCode(), stat.isUpdatable() ? "ResultSet.CONCUR_UPDATABLE" : "ResultSet.CONCUR_READ_ONLY");
+ for (int i = 1; i <= stat.getParameterMetaData().getParameterCount(); i++) {
+ final int t = stat.getParameterMetaData().getParameterType(i);
+ if (stat.getParameterMetaData().isNullable(i) != ParameterMetaData.parameterNoNulls) {
+ out.printf("%s%s%s%sif (p%d.isBlock()) {\n",
+ tabs, tabs, tabs, tabs,
+ i);
+ out.printf("%s%s%s%s%sps.set%s(%d, %s);\n",
+ tabs, tabs, tabs, tabs, tabs,
+ getAccessor(t), i, getConverter(t, "p" + i + ".asBlock().get(0)"));
+ out.printf("%s%s%s%s} else {\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s%sps.setNull(%d, %d);\n",
+ tabs, tabs, tabs, tabs, tabs,
+ i, t);
+ out.printf("%s%s%s%s}\n", tabs, tabs, tabs, tabs);
+ } else {
+ out.printf("%s%s%s%sps.set%s(%d, %s);\n",
+ tabs, tabs, tabs, tabs,
+ getAccessor(t), i, getConverter(t, "p" + i));
+ } // end if/else
+ } // end for
+ switch (stat.getKind()) {
+ case COMMAND:
+ out.printf("%s%s%s%sres.setInt32(ps.executeUpdate());\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%sreturn Value.createFromBlock(res);\n", tabs, tabs, tabs, tabs);
+ break;
+ case QUERY:
+ out.printf("%s%s%s%sreturn Cadmium.createObject(ps.executeQuery());\n", tabs, tabs, tabs, tabs);
+ break;
+ default:
+ // should never be reached
+ assert false : "invalid case";
+ break;
+ } // end switch
+ out.printf("%s%s%s} catch (final Exception e) {\n", tabs, tabs, tabs);
+ out.printf("%s%s%s%sCadmium.fail(ctxt, e);\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%sreturn Value.UNIT;\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s}\n", tabs, tabs, tabs);
+ out.printf("%s%s} else {\n", tabs, tabs);
+ if (stat.isPrepared()) {
+ out.printf("%s%s%sfinal JDBCSlot slot = getSlot(ctxt);\n", tabs, tabs, tabs);
+ if (stat.getKind() == SQLStatement.Kind.COMMAND) {
+ out.printf("%s%s%sfinal Block res = Block.createCustom(Custom.INT_32_SIZE, Custom.INT_32_OPS);\n", tabs, tabs, tabs);
+ } // end if
+ out.printf("%s%s%stry {\n", tabs, tabs, tabs);
+ out.printf("%s%s%s%sif (slot == null) {\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s%sthrow new SQLException(\"not connected\");\n", tabs, tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s}\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%sfinal PreparedStatement ps = slot.getStatement(%d);\n", tabs, tabs, tabs, tabs, index);
+ for (int i = 1; i <= stat.getParameterMetaData().getParameterCount(); i++) {
+ final int t = stat.getParameterMetaData().getParameterType(i);
+ if (stat.getParameterMetaData().isNullable(i) != ParameterMetaData.parameterNoNulls) {
+ out.printf("%s%s%s%sif (p%d.isBlock()) {\n",
+ tabs, tabs, tabs, tabs,
+ i);
+ out.printf("%s%s%s%s%sps.set%s(%d, %s);\n",
+ tabs, tabs, tabs, tabs, tabs,
+ getAccessor(t), i, getConverter(t, "p" + i + ".asBlock().get(0)"));
+ out.printf("%s%s%s%s} else {\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%s%sps.setNull(%d, %d);\n",
+ tabs, tabs, tabs, tabs, tabs,
+ i, t);
+ out.printf("%s%s%s%s}\n", tabs, tabs, tabs, tabs);
+ } else {
+ out.printf("%s%s%s%sps.set%s(%d, %s);\n",
+ tabs, tabs, tabs, tabs,
+ getAccessor(t), i, getConverter(t, "p" + i));
+ } // end if/else
+ } // end for
+ switch (stat.getKind()) {
+ case COMMAND:
+ out.printf("%s%s%s%sres.setInt32(ps.executeUpdate());\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%sreturn Value.createFromBlock(res);\n", tabs, tabs, tabs, tabs);
+ break;
+ case QUERY:
+ out.printf("%s%s%s%sreturn Cadmium.createObject(ps.executeQuery());\n", tabs, tabs, tabs, tabs);
+ break;
+ default:
+ // should never be reached
+ assert false : "invalid case";
+ break;
+ } // end switch
+ out.printf("%s%s%s} catch (final Exception e) {\n", tabs, tabs, tabs);
+ out.printf("%s%s%s%sCadmium.fail(ctxt, e);\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s%sreturn Value.UNIT;\n", tabs, tabs, tabs, tabs);
+ out.printf("%s%s%s}\n", tabs, tabs, tabs);
+ out.printf("%s%s}\n", tabs, tabs);
+ } else {
+ out.printf("%s%s%sFail.invalidArgument(\"invalid connection\");\n",
+ tabs, tabs, tabs);
+ out.printf("%s%s%sreturn Value.UNIT;\n", tabs, tabs, tabs);
+ out.printf("%s%s}\n", tabs, tabs);
+ } // end if/else
+ out.printf("%s}\n", tabs);
+ out.printf("\n");
+ if (1 + stat.getParameterMetaData().getParameterCount() > 5) {
+ forSynonym(out,
+ prefix + stat.getName(),
+ prefix + stat.getName() + "_bytecode",
+ 1 + stat.getParameterMetaData().getParameterCount(),
+ tabs);
+ } // end if
+ } // end method 'forStatement(PrintStream, String, int, SQLStatement, String)'
+
+ /**
+ * Generates the Java code for private utility methods.
+ * @param out where to print generated code - should not be null
+ * @param module module name - should not be null
+ * @param tabs tabulation value - should not be null
+ */
+ static void forUtilityMethods(final PrintStream out,
+ final String module,
+ final String tabs) {
+ assert out != null : "null out";
+ assert module != null : "null module";
+ assert tabs != null : "null tabs";
+ out.printf("%sprivate static JDBCSlot getSlot(final CodeRunner ctxt) {\n", tabs);
+ out.printf("%s%sreturn (JDBCSlot) ctxt.getContext().getSlot(%s.class);\n",
+ tabs, tabs, module);
+ out.printf("%s}\n", tabs);
+ out.printf("\n");
+ } // end method 'forUtilityMethods(PrintStream, String, String)'
+
+ /**
+ * Generates the Java code for a bytecode primitive being the synonym of a
+ * given native primitive.
+ * @param out where to print generated code - should not be null
+ * @param nativeId synonym identifier - should not be null
+ * @param bytecodeId primitive identifier - should not be null
+ * @param params number of primitive parameters
+ * - should be > 0
+ * @param tabs tabulation value - should not be null
+ */
+ static void forSynonym(final PrintStream out,
+ final String nativeId,
+ final String bytecodeId,
+ final int params,
+ final String tabs) {
+ assert out != null : "null out";
+ assert nativeId != null : "null nativeId";
+ assert bytecodeId != null : "null bytecodeId";
+ assert params >= 0 : "params should be > 0";
+ assert tabs != null : "null tabs";
+
+ out.printf("%s@Primitive\n", tabs);
+ out.printf("%spublic static Value %s(final CodeRunner ctxt",
+ tabs,
+ bytecodeId);
+ for (int i = 0; i < params; i++) {
+ out.printf(", final Value p%d", i);
+ } // end for
+ out.printf(") throws Fail.Exception {\n");
+ out.printf("%s%sreturn %s(ctxt", tabs, tabs, nativeId);
+ for (int i = 0; i < params; i++) {
+ out.printf(", p%d", i);
+ } // end for
+ out.printf(");\n");
+ out.printf("%s}\n", tabs);
+ out.printf("\n");
+ } // end method 'forSynonym(PrintStream, String, String, int, String)'
+
+ /**
+ * Generates the Java code for a primitive provider footer.
+ * @param out where to print generated code - should not be null
+ */
+ static void forFooter(final PrintStream out) {
+ assert out != null : "null out";
+ out.printf("}\n");
+ } // end method 'forFooter(PrintStream)'
+
+ /**
+ * Returns the accessor name for a given JDBC type.
+ * @param type JDBC type
+ * @return the accessor name for the passed JDBC type
+ */
+ private static String getAccessor(final int type) {
+ switch (type) {
+ case Types.ARRAY:
+ return "Array";
+ case Types.BIGINT:
+ return "Long";
+ case Types.BINARY:
+ return "Bytes";
+ case Types.BIT:
+ return "Boolean";
+ case Types.BLOB:
+ return "Blob";
+ case Types.BOOLEAN:
+ return "Boolean";
+ case Types.CHAR:
+ return "String";
+ case Types.CLOB:
+ return "Clob";
+ case Types.DATALINK:
+ return "URL";
+ case Types.DATE:
+ return "Date";
+ case Types.DECIMAL:
+ return "BigDecimal";
+ case Types.DISTINCT:
+ return "Object";
+ case Types.DOUBLE:
+ return "Double";
+ case Types.FLOAT:
+ return "Float";
+ case Types.INTEGER:
+ return "Int";
+ case Types.JAVA_OBJECT:
+ return "Object";
+ case Types.LONGNVARCHAR:
+ return "NString";
+ case Types.LONGVARBINARY:
+ return "Bytes";
+ case Types.LONGVARCHAR:
+ return "String";
+ case Types.NCHAR:
+ return "NString";
+ case Types.NCLOB:
+ return "NClob";
+ case Types.NUMERIC:
+ return "BigDecimal";
+ case Types.NVARCHAR:
+ return "NString";
+ case Types.OTHER:
+ return "Object";
+ case Types.REAL:
+ return "Double";
+ case Types.REF:
+ return "Ref";
+ case Types.ROWID:
+ return "RowId";
+ case Types.SMALLINT:
+ return "Short";
+ case Types.SQLXML:
+ return "SQLXML";
+ case Types.STRUCT:
+ return "Struct";
+ case Types.TIME:
+ return "Time";
+ case Types.TIMESTAMP:
+ return "Timestamp";
+ case Types.TINYINT:
+ return "Byte";
+ case Types.VARBINARY:
+ return "Bytes";
+ case Types.VARCHAR:
+ return "String";
+ default:
+ // should not happen
+ assert false : "invalid case";
+ return "";
+ } // end switch
+ } // end method 'getAccessor(int)'
+
+ /**
+ * Returns the Java code converting an OCaml value into a given JDBC type.
+ * @param type JDBC type
+ * @param name of the variable holding the OCaml value - should not be null
+ * @return the Java code converting the passed value into the given JDBC type
+ */
+ private static String getConverter(final int type, final String name) {
+ switch (type) {
+ case Types.ARRAY:
+ return name + ".asBlock().asCustom()";
+ case Types.BIGINT:
+ return name + ".asBlock().asInt64()";
+ case Types.BINARY:
+ return name + ".asBlock().getBytes()";
+ case Types.BIT:
+ return name + " == Value.TRUE";
+ case Types.BLOB:
+ return "(Blob) " + name + ".asBlock().asCustom()";
+ case Types.BOOLEAN:
+ return name + " == Value.TRUE";
+ case Types.CHAR:
+ return name + ".asBlock().asString()";
+ case Types.CLOB:
+ return "(Clob) " + name + ".asBlock().asCustom()";
+ case Types.DATALINK:
+ return name + ".asBlock().asString()";
+ case Types.DATE:
+ return "(Date) " + name + ".asBlock().asCustom()";
+ case Types.DECIMAL:
+ return "(BigDecimal) " + name + ".asBlock().asCustom()";
+ case Types.DISTINCT:
+ return name + ".asBlock().asCustom()";
+ case Types.DOUBLE:
+ return name + ".asBlock().asDouble()";
+ case Types.FLOAT:
+ return "(float) " + name + ".asBlock().asDouble()";
+ case Types.INTEGER:
+ return name + ".asBlock().asInt32()";
+ case Types.JAVA_OBJECT:
+ return name + ".asBlock().asCustom()";
+ case Types.LONGNVARCHAR:
+ return name + ".asBlock().asString()";
+ case Types.LONGVARBINARY:
+ return name + ".asBlock().getBytes()";
+ case Types.LONGVARCHAR:
+ return name + ".asBlock().asString()";
+ case Types.NCHAR:
+ return name + ".asBlock().asString()";
+ case Types.NCLOB:
+ return "(NClob) " + name + ".asBlock().asCustom()";
+ case Types.NUMERIC:
+ return "(BigDecimal) " + name + ".asBlock().asCustom()";
+ case Types.NVARCHAR:
+ return name + ".asBlock().asString()";
+ case Types.OTHER:
+ return name + ".asBlock().asCustom()";
+ case Types.REAL:
+ return name + ".asBlock().asDouble()";
+ case Types.REF:
+ return "(Ref) " + name + ".asBlock().asCustom()";
+ case Types.ROWID:
+ return "(RowId) " + name + ".asBlock().asCustom()";
+ case Types.SMALLINT:
+ return "(short) " + name + ".asLong()";
+ case Types.SQLXML:
+ return "(SQLXML) " + name + ".asBlock().asCustom()";
+ case Types.STRUCT:
+ return name + ".asBlock().asCustom()";
+ case Types.TIME:
+ return "(Time) " + name + ".asBlock().asCustom()";
+ case Types.TIMESTAMP:
+ return "(Timestamp) " + name + ".asBlock().asCustom()";
+ case Types.TINYINT:
+ return "(byte) " + name + ".asLong()";
+ case Types.VARBINARY:
+ return name + ".asBlock().getBytes()";
+ case Types.VARCHAR:
+ return name + ".asBlock().asString()";
+ default:
+ // should not happen
+ assert false : "invalid case";
+ return name;
+ } // end switch
+ } // end method 'getConverter(int, String)'
+
+} // end class 'GenerateJavaCode'
addfile ./src/fr/x9c/nickel/database/ParseXML.java
hunk ./src/fr/x9c/nickel/database/ParseXML.java 1
+/*
+ * This file is part of Nickel.
+ * Copyright (C) 2007-2008 Xavier Clerc.
+ *
+ * Nickel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Nickel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package fr.x9c.nickel.database;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import fr.x9c.nickel.CheckString;
+import fr.x9c.nickel.NickelException;
+import fr.x9c.nickel.StrictErrorHandler;
+
+/**
+ * This class implements a method parsing XML data (using dbmodule DTD)
+ * into a list of {@link fr.x9c.nickel.database.SQLStatement} objects.
+ *
+ * @author Xavier Clerc
+ * @version 1.1
+ * @since 1.1
+ */
+final class ParseXML {
+
+ /** Message printed when xml parse starts, if verbose. */
+ private static final String XML_PARSE_TRACE = "xml parse ...";
+
+ /** Directory of DTDs. */
+ private static final String DTD_DIRECTORY = "/dtds";
+
+ /** DTD name. */
+ private static final String DTD_NAME = "dbmodule";
+
+ /** DTD complete system ID. */
+ private static final String DTD_SYSTEM_ID = "dtds/dbmodule.dtd";
+
+ /** Message for XML error. */
+ private static final String XML_ERROR = "error during xml parsing";
+
+ /** Message for DTD error. */
+ private static final String DTD_ERROR = "wrong dtd is used";
+
+ /** Message for XML parser error. */
+ private static final String PARSER_ERROR = "cannot get parser";
+
+ /** Message for i/o error. */
+ private static final String IO_ERROR = "i/o error";
+
+ /** Message for invalid OCaml module name. */
+ private static final String INVALID_OCAML_MODULE_NAME =
+ "invalid OCaml module name: '%s'";
+
+ /** Message for invalid meta name. */
+ private static final String INVALID_META_NAME =
+ "invalid meta name: '%s'";
+
+ /** Message for invalid OCaml name. */
+ private static final String INVALID_NAME =
+ "invalid name: '%s'";
+
+ /**
+ * No instance of this class.
+ */
+ private ParseXML() {
+ } // end empty constructor
+
+ /**
+ * Parses XML data from a stream using 'dbmodule.dtd' DTD and converts it into
+ * a list of statements to be processed.
+ * @param stream stream to parse data from - should not be null
+ * closed before return
+ * @param statements list to add statements to - should not be null
+ * @param meta map to add meta definitions to - should not be null
+ * @param verbose where comments should be printed during process
+ * (null if such print should not occur)
+ * @param debug where debug elements (e.g. stack traces)
+ * should be printed when an error occurs
+ * (null if such print should not occur)
+ * @return module name
+ * @throws NickelException if an error occurs during parse or conversion
+ * @throws NickelException if an i/o error occurs
+ */
+ static String parse(final InputStream stream,
+ final List statements,
+ final Map< String, List > meta,
+ final PrintStream verbose,
+ final PrintStream debug)
+ throws NickelException {
+ assert stream != null : "null stream";
+ assert statements != null : "null statements";
+ assert meta != null : "null meta";
+
+ if (verbose != null) {
+ verbose.println(ParseXML.XML_PARSE_TRACE);
+ } // end if
+
+ try {
+ final DocumentBuilderFactory factory =
+ DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(false);
+ factory.setValidating(true);
+ final DocumentBuilder builder = factory.newDocumentBuilder();
+ builder.setErrorHandler(new StrictErrorHandler());
+ final String systemID =
+ ParseXML.class.getResource(ParseXML.DTD_DIRECTORY).toString();
+ final Document doc = builder.parse(stream, systemID);
+ final DocumentType type = doc.getDoctype();
+ if (!type.getName().equals(ParseXML.DTD_NAME)
+ || (type.getPublicId() != null)
+ || !type.getSystemId().equals(ParseXML.DTD_SYSTEM_ID)) {
+ throw new NickelException(NickelException.Phase.XML_PARSE,
+ ParseXML.DTD_ERROR);
+ } // end if
+ return processDocument(doc, statements, meta, verbose, debug);
+ } catch (final SAXException se) {
+ final Exception e = se.getException();
+ final String msg;
+ if (e != null) {
+ msg = e.getMessage();
+ if (debug != null) {
+ e.printStackTrace(debug);
+ } // end if
+ } else {
+ msg = se.getMessage();
+ if (debug != null) {
+ se.printStackTrace(debug);
+ } // end if
+ } // end if/else
+ throw new NickelException(NickelException.Phase.XML_PARSE,
+ msg != null ? msg : ParseXML.XML_ERROR);
+ } catch (final ParserConfigurationException pce) {
+ if (debug != null) {
+ pce.printStackTrace(debug);
+ } // end if
+ final String msg = pce.getMessage();
+ throw new NickelException(NickelException.Phase.XML_PARSE,
+ msg != null ? msg : ParseXML.PARSER_ERROR);
+ } catch (final IOException ioe) {
+ if (debug != null) {
+ ioe.printStackTrace(debug);
+ } // end if
+ final String msg = ioe.getMessage();
+ throw new NickelException(NickelException.Phase.XML_PARSE,
+ msg != null ? msg : ParseXML.IO_ERROR);
+ } catch (final FactoryConfigurationError fce) {
+ if (debug != null) {
+ fce.printStackTrace(debug);
+ } // end if
+ final String msg = fce.getMessage();
+ throw new NickelException(NickelException.Phase.XML_PARSE,
+ msg != null ? msg : ParseXML.PARSER_ERROR);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException ioe) {
+ // nothing to do on simple cleanup
+ } // end try/catch
+ } // end try/catch/finally
+ } // end method 'parse(InputStream, List, Map< String, List >, PrintStream, PrintStream)'
+
+ /**
+ * Processes a document node.
+ * @param doc document node to process - should not be null
+ * @param statements list to add statements to - should not be null
+ * @param meta map to add meta definitions to - should not be null
+ * @param verbose where comments should be printed during process
+ * (null if such print should not occur)
+ * @param debug where debug elements (e.g. stack traces)
+ * should be printed when an error occurs
+ * (null if such print should not occur)
+ * @return module name
+ * @throws NickelException if any error occurs
+ */
+ private static String processDocument(final Document doc,
+ final List statements,
+ final Map< String, List > meta,
+ final PrintStream verbose,
+ final PrintStream debug)
+ throws NickelException {
+ assert doc != null : "null doc";
+ assert statements != null : "null statements";
+ assert meta != null : "null meta";
+ final NodeList children = doc.getElementsByTagName("dbmodule");
+ if (children.getLength() == 1) {
+ return processDbModule((Element) children.item(0),
+ statements,
+ meta,
+ verbose,
+ debug);
+ } else {
+ throw new NickelException(NickelException.Phase.XML_PARSE,
+ ParseXML.DTD_ERROR);
+ } // end if/else
+ } // end method 'processDocument(Document, List, Map< String, List >, PrintStream, PrintStream)'
+
+ /**
+ * Processes a "dbmodule" node.
+ * @param module module node to process - should not be null
+ * @param statements list to add statements to - should not be null
+ * @param meta map to add meta definitions to - should not be null
+ * @param verbose where comments should be printed during process
+ * (null if such print should not occur)
+ * @param debug where debug elements (e.g. stack traces)
+ * should be printed when an error occurs
+ * (null if such print should not occur)
+ * @return module name
+ * @throws NickelException if any error occurs
+ */
+ private static String processDbModule(final Element module,
+ final List statements,
+ final Map< String, List > meta,
+ final PrintStream verbose,
+ final PrintStream debug)
+ throws NickelException {
+ assert module != null : "null module";
+ assert statements != null : "null statements";
+ assert meta != null : "null meta";
+
+ final String name = module.getAttribute("name");
+ if (!CheckString.forOCamlModuleName(name)) {
+ throw new NickelException(NickelException.Phase.XML_PARSE,
+ String.format(ParseXML.INVALID_OCAML_MODULE_NAME,
+ name));
+ } // end if
+ if (verbose != null) {
+ verbose.printf("... dbmodule '%s'\n", name);
+ } // end if
+
+ // "meta" elements
+ final NodeList metas = module.getElementsByTagName("meta");
+ final int lenMetas = metas.getLength();
+ for (int i = 0; i < lenMetas; i++) {
+ processMeta((Element) metas.item(i),
+ meta,
+ verbose,
+ debug);
+ } // end for
+
+ // "command" elements
+ final NodeList commands = module.getElementsByTagName("command");
+ final int lenCommands = commands.getLength();
+ for (int i = 0; i < lenCommands; i++) {
+ processCommand((Element) commands.item(i),
+ statements,
+ verbose,
+ debug);
+ } // end for
+
+ // "query" elements
+ final NodeList queries = module.getElementsByTagName("query");
+ final int lenQueries = queries.getLength();
+ for (int i = 0; i < lenQueries; i++) {
+ processQuery((Element) queries.item(i),
+ statements,
+ verbose,
+ debug);
+ } // end for
+
+ return name;
+ } // end method 'processDbModule(Element, List, Map< String, List >, PrintStream, PrintStream)'
+
+ /**
+ * Processes a "meta" node.
+ * @param met meta node to process - should not be null
+ * @param meta map to add meta definitions to - should not be null
+ * @param verbose where comments should be printed during process
+ * (null if such print should not occur)
+ * @param debug where debug elements (e.g. stack traces)
+ * should be printed when an error occurs
+ * (null if such print should not occur)
+ * @throws NickelException if any error occurs
+ */
+ private static void processMeta(final Element met,
+ final Map< String, List > meta,
+ final PrintStream verbose,
+ final PrintStream debug)
+ throws NickelException {
+ assert met != null : "null met";
+ assert meta != null : "null meta";
+
+ final String name = met.getAttribute("name");
+ if (!CheckString.forMetaName(name)) {
+ throw new NickelException(NickelException.Phase.XML_PARSE,
+ String.format(ParseXML.INVALID_META_NAME,
+ name));
+ } // end if
+ final String value = met.getAttribute("value");
+ final List list = meta.get(name);
+ if (list != null) {
+ list.add(value);
+ } else {
+ final List newList = new LinkedList();
+ newList.add(value);
+ meta.put(name, newList);
+ } // end if/else
+ if (verbose != null) {
+ verbose.printf("... meta named '%s' with value '%s'\n", name, value);
+ } // end if
+ } // end method 'processMeta(Element, Map< String, List >, PrintStream, PrintStream)'
+
+ /**
+ * Processes a "command" node.
+ * @param command command node to process - should not be null
+ * @param statements list to add statements to - should not be null
+ * @param verbose where comments should be printed during process
+ * (null if such print should not occur)
+ * @param debug where debug elements (e.g. stack traces)
+ * should be printed when an error occurs
+ * (null if such print should not occur)
+ * @throws NickelException if any error occurs
+ */
+ private static void processCommand(final Element command,
+ final List statements,
+ final PrintStream verbose,
+ final PrintStream debug)
+ throws NickelException {
+ assert command != null : "null command";
+ assert statements != null : "null statements";
+
+ final String name = command.getAttribute("name");
+ if (!CheckString.forOCamlClassName(name)) {
+ throw new NickelException(NickelException.Phase.XML_PARSE,
+ String.format(ParseXML.INVALID_NAME, name));
+ } // end if
+ final String code = command.getAttribute("code");
+ final boolean prepare = "yes".equals(command.getAttribute("prepare"));
+ statements.add(new SQLStatement(SQLStatement.Kind.COMMAND,
+ name,
+ code == null ? "" : code,
+ prepare,
+ false));
+ if (verbose != null) {
+ verbose.printf("... command '%s' with code '%s'\n", name, code);
+ } // end if
+ } // end method 'processCommand(Element, List, PrintStream, PrintStream)'
+
+ /**
+ * Processes a "query" node.
+ * @param query query node to process - should not be null
+ * @param statements list to add statements to - should not be null
+ * @param verbose where comments should be printed during process
+ * (null if such print should not occur)
+ * @param debug where debug elements (e.g. stack traces)
+ * should be printed when an error occurs
+ * (null if such print should not occur)
+ * @throws NickelException if any error occurs
+ */
+ private static void processQuery(final Element query,
+ final List statements,
+ final PrintStream verbose,
+ final PrintStream debug)
+ throws NickelException {
+ assert query != null : "null query";
+ assert statements != null : "null statements";
+
+ final String name = query.getAttribute("name");
+ if (!CheckString.forOCamlClassName(name)) {
+ throw new NickelException(NickelException.Phase.XML_PARSE,
+ String.format(ParseXML.INVALID_NAME, name));
+ } // end if
+ final String code = query.getAttribute("code");
+ final boolean prepare = "yes".equals(query.getAttribute("prepare"));
+ final boolean updatable = "yes".equals(query.getAttribute("updatable"));
+ statements.add(new SQLStatement(SQLStatement.Kind.QUERY,
+ name,
+ code == null ? "" : code,
+ prepare,
+ updatable));
+ if (verbose != null) {
+ verbose.printf("... query '%s' with code '%s'\n", name, code);
+ } // end if
+ } // end method 'processQuery(Element, List, PrintStream, PrintStream)'
+
+} // end class 'ParseXML'
addfile ./src/fr/x9c/nickel/database/SQLStatement.java
hunk ./src/fr/x9c/nickel/database/SQLStatement.java 1
+/*
+ * This file is part of Nickel.
+ * Copyright (C) 2007-2008 Xavier Clerc.
+ *
+ * Nickel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Nickel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package fr.x9c.nickel.database;
+
+import java.io.PrintStream;
+import java.sql.Connection;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+
+import fr.x9c.nickel.NickelException;
+
+/**
+ * This class defines a SQL statement to be run by a generated OCaml primitive.
+ *
+ * @author Xavier Clerc
+ * @version 1.1
+ * @since 1.1
+ */
+final class SQLStatement {
+
+ /** The different kinds of SQL statements. */
+ static enum Kind {
+
+ /** SQL statement that do not return a value. */
+ COMMAND,
+
+ /** SQL statement that return a value. */
+ QUERY
+ };
+
+ /** Beginning trace for statement resolution. */
+ private static final String RESOLVING_TRACE =
+ "resolving %s ...";
+
+ /** Trace for error during statement resolution. */
+ private static final String UNABLE_TO_EXECUTE =
+ "unable to execute %s: %s ...";
+
+ /** Kind of SQL statement. */
+ private final Kind kind;
+
+ /** Name of SQL statement. */
+ private final String name;
+
+ /** Code of SQL statement. */
+ private final String code;
+
+ /** Whether the SQL statement should be prepared upon connection. */
+ private final boolean prepared;
+
+ /** Whether the returned result sets should be updatable. */
+ private final boolean updatable;
+
+ /** Metadata for statement parameter, populated after (successful) resolution. */
+ private ParameterMetaData parameterMetaData;
+
+ /** Metadata for statement result set, populated after (successful) resolution. */
+ private ResultSetMetaData resultSetMetaData;
+
+ /**
+ * Constructs a SQL statement.
+ * @param k kind of statement - should not be null
+ * @param n name of statement - should not be null
+ * @param c code of statement - should not be null
+ * @param p whether the SQL statement should be prepared upon connection
+ * @param u whether the returned result sets should be updatable
+ */
+ SQLStatement(final Kind k,
+ final String n,
+ final String c,
+ final boolean p,
+ final boolean u) {
+ assert k != null : "null k";
+ assert n != null : "null n";
+ assert c != null : "null c";
+ assert (k == SQLStatement.Kind.QUERY) || !u : "incoherent parameters";
+ this.kind = k;
+ this.name = n;
+ this.code = c;
+ this.prepared = p;
+ this.updatable = u;
+ this.parameterMetaData = null;
+ this.resultSetMetaData = null;
+ } // end constructor(Kind, String, String, boolean, boolean)
+
+ /**
+ * Returns the kind of SQL statement.
+ * @return the kind of SQL statement
+ */
+ Kind getKind() {
+ return this.kind;
+ } // end method 'getKind()'
+
+ /**
+ * Returns the name of SQL statement.
+ * @return the name of SQL statement
+ */
+ String getName() {
+ return this.name;
+ } // end method 'getName()'
+
+ /**
+ * Returns the code of SQL statement.
+ * @return the code of SQL statement
+ */
+ String getCode() {
+ return this.code;
+ } // end method 'getCode()'
+
+ /**
+ * Tests whether the statement should be prepared upon connection.
+ * @return true if the statement should be prepared upon connection,
+ * false otherwise
+ */
+ boolean isPrepared() {
+ return this.prepared;
+ } // end method 'isPrepared()'
+
+ /**
+ * Tests whether the returned result sets should be updatable.
+ * @return true if the returned result sets should be updatable,
+ * false otherwise
+ */
+ boolean isUpdatable() {
+ return this.updatable;
+ } // end method 'isUpdatable()'
+
+ /**
+ * Returns the metadata for statement parameter.
+ * @return the metadata for statement parameter,
+ * null if resolution has not sucessfully completed
+ */
+ ParameterMetaData getParameterMetaData() {
+ return this.parameterMetaData;
+ } // end method 'getParameterMetaData()'
+
+ /**
+ * Returns the metadata for statement result set.
+ * @return the metadata for statement result set,
+ * null if resolution has not sucessfully completed
+ * or if statement does not return a value
+ */
+ ResultSetMetaData getResultSetMetaData() {
+ return this.resultSetMetaData;
+ } // end method 'getResultSetMetaData()'
+
+ /**
+ * Resolves a SQL, i.e. retrieves its related metadata.
+ * @param conn database connection - should not be null
+ * @param verbose where comments should be printed during process
+ * (null if such print should not occur)
+ * @param debug where debug elements (e.g. stack traces)
+ * should be printed when an error occurs
+ * (null if such print should not occur)
+ * @throws NickelException if an error occurs during resolution
+ */
+ void resolve(final Connection conn,
+ final PrintStream verbose,
+ final PrintStream debug)
+ throws NickelException {
+ assert conn != null : "null conn";
+ if (verbose != null) {
+ verbose.println(String.format(SQLStatement.RESOLVING_TRACE, this.name));
+ } // end if
+ try {
+ final PreparedStatement ps = conn.prepareStatement(this.code);
+ this.parameterMetaData = ps.getParameterMetaData();
+ switch (this.kind) {
+ case COMMAND:
+ this.resultSetMetaData = null;
+ break;
+ case QUERY:
+ this.resultSetMetaData = ps.getMetaData();
+ break;
+ default:
+ // should never be reached
+ assert false : "invalid case";
+ this.resultSetMetaData = null;
+ break;
+ } // end switch
+ } catch (final SQLException se) {
+ if (debug != null) {
+ se.printStackTrace(debug);
+ } // end if
+ throw new NickelException(NickelException.Phase.RESOLUTION,
+ String.format(SQLStatement.UNABLE_TO_EXECUTE,
+ this.name,
+ this.code));
+ } // end try/catch
+ } // end method 'resolve(Connection, PrintStream, PrintStream)'
+
+} // end class 'SQLStatement'
addfile ./src/fr/x9c/nickel/database/package.html
hunk ./src/fr/x9c/nickel/database/package.html 1
+
+This package contains the Nickel module producer whose goal is to generate both
+OCaml and Java sources (C source is also generated to allow compilation) to
+access to a database through a JDBC connection. These bridges are intended to be
+used with the Cadmium runtime.
+
}