[MAJOR: merge of 'Cadmium-Dbm'.
cadmium@x9c.fr**20080921113454] {
adddir ./src/fr/x9c/cadmium/primitives/dbm
hunk ./build.properties 33
+path.w3c-jdbm=${path.lib}/w3c-jdbm
hunk ./build.properties 45
+fr.x9c.cadmium.primitives.dbm,\
hunk ./build.xml 39
+
addfile ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 1
+/*
+ * This file is part of Cadmium.
+ * Copyright (C) 2007-2008 Xavier Clerc.
+ *
+ * Cadmium is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Cadmium 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package fr.x9c.cadmium.primitives.dbm;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.Enumeration;
+
+import org.w3c.tools.dbm.jdbm;
+
+import fr.x9c.cadmium.kernel.Block;
+import fr.x9c.cadmium.kernel.CodeRunner;
+import fr.x9c.cadmium.kernel.Fail;
+import fr.x9c.cadmium.kernel.FalseExit;
+import fr.x9c.cadmium.kernel.Primitive;
+import fr.x9c.cadmium.kernel.PrimitiveProvider;
+import fr.x9c.cadmium.kernel.Value;
+
+/**
+ * Implements all primitives from 'cldbm.c'.
+ *
+ * @author Xavier Clerc
+ * @version 1.1
+ * @since 1.1
+ */
+@PrimitiveProvider
+public final class Cldbm {
+
+ /** Suffix for database files. */
+ private static final String FILE_SUFFIX = ".db";
+
+ /**
+ * No instance of this class.
+ */
+ private Cldbm() {
+ } // end empty constructor
+
+ /**
+ * Opens a database.
+ * @param ctxt context
+ * @param file database file
+ * @param flags ignored (O_RDWR | O_CREAT is used)
+ * @param mode ignored
+ * @return database handle
+ * @throws Fail.Exception Dbm.Dbm_error if the file cannot be
+ * opened/created
+ */
+ @Primitive
+ public static Value caml_dbm_open(final CodeRunner ctxt,
+ final Value file,
+ final Value flags,
+ final Value mode)
+ throws Fail.Exception, FalseExit {
+ try {
+ return createDbm(new jdbm(file.asBlock().asString()
+ + Cldbm.FILE_SUFFIX));
+ } catch (final InterruptedIOException iioe) {
+ final FalseExit fe =
+ FalseExit.createFromContext(ctxt.getContext());
+ fe.fillInStackTrace();
+ throw fe;
+ } catch (final IOException ioe) {
+ error(ctxt, "Can't open/create file");
+ return Value.UNIT; // never reached
+ } catch (final Exception e) {
+ error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'caml_dbm_open(CodeRunner, Value, Value, Value)'
+
+ /**
+ * Closes a database.
+ * @param ctxt context
+ * @param db database handle
+ * @return unit
+ * @throws Fail.Exception Dbm.Dbm_error if the database is already
+ * closed
+ */
+ @Primitive
+ public static Value caml_dbm_close(final CodeRunner ctxt,
+ final Value db)
+ throws Fail.Exception, FalseExit {
+ try {
+ get(ctxt, db).db.save();
+ } catch (final InterruptedIOException iioe) {
+ final FalseExit fe =
+ FalseExit.createFromContext(ctxt.getContext());
+ fe.fillInStackTrace();
+ throw fe;
+ } catch (final IOException ioe) {
+ // nothing to do ...
+ } catch (final Fail.Exception fe) {
+ throw fe;
+ } catch (final Exception e) {
+ // nothing to do ...
+ } // end try/catch
+ db.asBlock().setCustom(null);
+ return Value.UNIT;
+ } // end method 'caml_dbm_close(CodeRunner, Value)'
+
+ /**
+ * Gets a value using its key.
+ * @param ctxt context
+ * @param db database handle
+ * @param key key
+ * @return data associated with passed key
+ * @throws Fail.Exception Not_found if no entry exists for the
+ * passed key
+ * @throws Fail.Exception Dbm.Dbm_error if the database is already
+ * closed
+ * @throws Fail.Exception Dbm.Dbm_error if an i/o error occurs
+ */
+ @Primitive
+ public static Value caml_dbm_fetch(final CodeRunner ctxt,
+ final Value db,
+ final Value key)
+ throws Fail.Exception, FalseExit {
+ final jdbm database = get(ctxt, db).db;
+ try {
+ final byte[] res = database.fetch(key.asBlock().getBytes());
+ if (res != null) {
+ return Value.createFromBlock(Block.createString(res));
+ } else {
+ Fail.raiseNotFound();
+ return Value.UNIT; // never reached
+ } // end if/else
+ } catch (final InterruptedIOException iioe) {
+ final FalseExit fe =
+ FalseExit.createFromContext(ctxt.getContext());
+ fe.fillInStackTrace();
+ throw fe;
+ } catch (final IOException ioe) {
+ error(ctxt, "dbm_fetch failed");
+ return Value.UNIT; // never reached
+ } catch (final Fail.Exception fe) {
+ throw fe;
+ } catch (final Exception e) {
+ error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'caml_dbm_fetch(CodeRunner, Value, Value)'
+
+ /**
+ * Inserts a (key, data) pair in a database.
+ * @param ctxt context
+ * @param db database handle
+ * @param key key
+ * @param content data associated with key
+ * @return unit
+ * @throws Fail.Exception Dbm.Dbm_error if the database is already
+ * closed
+ * @throws Fail.Exception Dbm.Dbm_error if an i/o error occurs
+ * @throws Fail.Exception Dbm.Dbm_error if an entry with the same
+ * key already exists
+ */
+ @Primitive
+ public static Value caml_dbm_insert(final CodeRunner ctxt,
+ final Value db,
+ final Value key,
+ final Value content)
+ throws Fail.Exception, FalseExit {
+ final jdbm database = get(ctxt, db).db;
+ try {
+ final byte[] res = database.fetch(key.asBlock().getBytes());
+ if (res != null) {
+ error(ctxt, "Entry already exists");
+ return Value.UNIT; // never reached
+ } else {
+ database.store(key.asBlock().getBytes(),
+ content.asBlock().getBytes(),
+ jdbm.STORE_INSERT);
+ return Value.UNIT;
+ } // end if/else
+ } catch (final InterruptedIOException iioe) {
+ final FalseExit fe =
+ FalseExit.createFromContext(ctxt.getContext());
+ fe.fillInStackTrace();
+ throw fe;
+ } catch (final IOException ioe) {
+ error(ctxt, "dbm_store failed");
+ return Value.UNIT; // never reached
+ } catch (final Fail.Exception fe) {
+ throw fe;
+ } catch (final Exception e) {
+ error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'caml_dbm_insert(CodeRunner, Value, Value, Value)'
+
+ /**
+ * Inserts a (key, data) pair in a database, replacing current value
+ * if a pair with same key already exists.
+ * @param ctxt context
+ * @param db database handle
+ * @param key key
+ * @param content data associated with key
+ * @return unit
+ * @throws Fail.Exception Dbm.Dbm_error if the database is already
+ * closed
+ * @throws Fail.Exception Dbm.Dbm_error if an i/o error occurs
+ */
+ @Primitive
+ public static Value caml_dbm_replace(final CodeRunner ctxt,
+ final Value db,
+ final Value key,
+ final Value content)
+ throws Fail.Exception, FalseExit {
+ final jdbm database = get(ctxt, db).db;
+ try {
+ database.store(key.asBlock().getBytes(),
+ content.asBlock().getBytes(),
+ jdbm.STORE_REPLACE);
+ return Value.UNIT;
+ } catch (final InterruptedIOException iioe) {
+ final FalseExit fe =
+ FalseExit.createFromContext(ctxt.getContext());
+ fe.fillInStackTrace();
+ throw fe;
+ } catch (final IOException ioe) {
+ error(ctxt, "dbm_store failed");
+ return Value.UNIT; // never reached
+ } catch (final Exception e) {
+ error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'caml_dbm_replace(CodeRunner, Value, Value, Value)'
+
+ /**
+ * Deletes a value using its key.
+ * @param ctxt context
+ * @param db database handle
+ * @param key key
+ * @return unit
+ * @throws Fail.Exception Dbm.Dbm_error if the database is already
+ * closed
+ * @throws Fail.Exception Dbm.Dbm_error if an i/o error occurs
+ */
+ @Primitive
+ public static Value caml_dbm_delete(final CodeRunner ctxt,
+ final Value db,
+ final Value key)
+ throws Fail.Exception, FalseExit {
+ final jdbm database = get(ctxt, db).db;
+ try {
+ database.delete(key.asBlock().getBytes());
+ return Value.UNIT;
+ } catch (final InterruptedIOException iioe) {
+ final FalseExit fe =
+ FalseExit.createFromContext(ctxt.getContext());
+ fe.fillInStackTrace();
+ throw fe;
+ } catch (final IOException ioe) {
+ error(ctxt, "dbm_delete failed");
+ return Value.UNIT; // never reached
+ } catch (final Exception e) {
+ error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'caml_dbm_delete(CodeRunner, Value, Value)'
+
+ /**
+ * Returns the first key of a database.
+ * @param ctxt context
+ * @param db database handle
+ * @return the first key of the passed database
+ * @throws Fail.Exception Dbm.Dbm_error if the database is already
+ * closed
+ * @throws Fail.Exception Dbm.Dbm_error if such a key does not
+ * exist
+ */
+ @Primitive
+ public static Value caml_dbm_firstkey(final CodeRunner ctxt,
+ final Value db)
+ throws Fail.Exception {
+ final DB database = get(ctxt, db);
+ final Enumeration e = database.db.keys();
+ database.enumeration = e;
+ if (!e.hasMoreElements()) {
+ Fail.raiseNotFound();
+ } // end if
+ return Value.createFromBlock(Block.createString((byte[]) e.nextElement()));
+ } // end method 'caml_dbm_firstkey(CodeRunner, Value)'
+
+ /**
+ * Returns the next key of a database.
+ * @param ctxt context
+ * @param db database handle
+ * @return the next key of the passed database
+ * @throws Fail.Exception Dbm.Dbm_error if the database is already
+ * closed
+ * @throws Fail.Exception Dbm.Dbm_error if such a key does not
+ * exist
+ */
+ @Primitive
+ public static Value caml_dbm_nextkey(final CodeRunner ctxt,
+ final Value db)
+ throws Fail.Exception {
+ final Enumeration e = get(ctxt, db).enumeration;
+ if ((e == null) || !e.hasMoreElements()) {
+ Fail.raiseNotFound();
+ } // end if
+ return Value.createFromBlock(Block.createString((byte[]) e.nextElement()));
+ } // end method 'caml_dbm_nextkey(CodeRunner, Value)'
+
+ /**
+ * Creates a Dbm.t instance by encapsulating a
+ * {@link fr.x9c.cadmium.primitives.dbm.Cldbm.DB} object into an abstract
+ * value.
+ * @param db object to encapsulate - should not be null
+ * @return passed object encapsulated into an abstract value
+ */
+ private static Value createDbm(final jdbm db) {
+ assert db != null : "null db";
+ final Block res = Block.createBlock(1, Block.ABSTRACT_TAG);
+ res.setCustom(new DB(db));
+ return Value.createFromBlock(res);
+ } // end method 'createDbm(jdbm)'
+
+ /**
+ * Returns the {@link fr.x9c.cadmium.primitives.dbm.Cldbm.DB} object
+ * encapsulated inside a value.
+ * @param ctxt context - should not be null
+ * @param db value to get jdbm object from
+ * - should not be null
+ * @return the {@link fr.x9c.cadmium.primitives.dbm.Cldbm.DB} object
+ * encapsulated inside a value
+ * @throws Fail.Exception if db has been closed
+ */
+ private static DB get(final CodeRunner ctxt,
+ final Value db)
+ throws Fail.Exception {
+ assert ctxt != null : "null ctxt";
+ assert db != null : "null db";
+ final DB res = (DB) db.asBlock().asCustom();
+ if (res != null) {
+ return res;
+ } else {
+ error(ctxt, "DBM has been closed");
+ return null; // never reached
+ } // end if/else
+ } // end method 'get(CodeRunner, Value)'
+
+ /**
+ * Raises a Dbm.Dbm_error exception.
+ * @param ctxt context - should not be null
+ * @param msg exception message - should not be null
+ * @throws Fail.Exception always
+ */
+ private static void error(final CodeRunner ctxt,
+ final String msg)
+ throws Fail.Exception {
+ assert ctxt != null : "null ctxt";
+ assert msg != null : "null msg";
+ final Value dbmException = ctxt.getContext().getCallback("dbmerror");
+ if (dbmException == null) {
+ Fail.invalidArgument("Dbm.Dbm_error is not initialized");
+ } else {
+ Fail.raiseWithString(dbmException, msg);
+ } // end if/else
+ } // end method 'error(CodeRunner, String)'
+
+ /**
+ * This inner-class is a mere structure describing a
+ * (database, enumeration) pair.
+ */
+ private static final class DB {
+
+ /** Actual database. */
+ private final jdbm db;
+
+ /** Current enumeration over keys. */
+ private Enumeration enumeration;
+
+ /**
+ * Constructs a structure from a database.
+ * Enumeration is set to null.
+ * @param d handle to database - should not be null
+ */
+ private DB(final jdbm d) {
+ assert d != null : "null d";
+ this.db = d;
+ this.enumeration = null;
+ } // end constructor(jdbm)
+
+ } // end inner-class 'DB'
+
+} // end class 'Cldbm'
addfile ./src/fr/x9c/cadmium/primitives/dbm/package.html
hunk ./src/fr/x9c/cadmium/primitives/dbm/package.html 1
+
+This package contains the classes providing implementation for 'dbm' primitives.
+
+This implementation does not guarantee that produced 'dot db' files are
+compatible with the reference implementation.
+This implementation is based on the 'jdbm' part of the Jigsaw project.
+It thus requires the org.w3c.tools.dbm package to be available in
+the classpath. This package is available from the following URLs:
+
+ - http://aurora.regenstrief.org/~schadow/dbm-java/
+
- http://dev.w3.org/cvsweb/java/classes/org/w3c/tools/dbm/#dirlist
+
+
+This implementation is provided for compatibility purpose only and a Cadmium
+user is strongly encouraged to use Nickel
+in order to access to the full power of JDBC.
+
}