[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: + +

+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. + }