[MAJOR: two Dbm implementations are now provided: a jdbm-based one (originally from Cadmium-Dbm) and a java.util-based one.
cadmium@x9c.fr**20081001193337] {
hunk ./build.xml 73
+
hunk ./build.xml 116
- includes="**/*.class"
+ includes="**/*.class,**/*.clazz"
hunk ./src/fr/x9c/cadmium/kernel/Libraries.java 124
+ /** Primitive providers for 'otherlibs/dbm'. */
+ String[] DBM = {
+ "fr.x9c.cadmium.primitives.dbm.Cldbm"
+ };
+
hunk ./src/fr/x9c/cadmium/kernel/Libraries.java 271
+ DBM,
addfile ./src/fr/x9c/cadmium/primitives/dbm/BasicImplementation.java
hunk ./src/fr/x9c/cadmium/primitives/dbm/BasicImplementation.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.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+
+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.Misc;
+import fr.x9c.cadmium.kernel.Value;
+
+/**
+ * This interface defines a Dbm implementation based on a java.util.Map.
+ *
+ * @author Xavier Clerc
+ * @version 1.1
+ * @since 1.1
+ */
+final class BasicImplementation implements DbmImplementation {
+
+ /** Suffix for database files. */
+ private static final String FILE_SUFFIX = ".db";
+
+ /** Flag allowing to read data. */
+ private static final int READ = 1;
+
+ /** Flag allowing to write data. */
+ private static final int WRITE = 2;
+
+ /** Flag allowing to create data file. */
+ private static final int CREATE = 4;
+
+ /** Flags for Dbm operations. */
+ private static final int[] FLAGS = {
+ READ,
+ WRITE,
+ READ | WRITE,
+ CREATE
+ };
+
+ /**
+ * Empty constructor.
+ */
+ BasicImplementation() {
+ } // end empty constructor
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value open(final CodeRunner ctxt,
+ final Value file,
+ final Value flags,
+ final Value mode)
+ throws Fail.Exception, FalseExit {
+ try {
+ final File f = ctxt.getContext().getRealFile(file.asBlock().asString()
+ + BasicImplementation.FILE_SUFFIX);
+
+ return createDbm(f, Misc.convertFlagList(flags, BasicImplementation.FLAGS));
+ } catch (final InterruptedIOException iioe) {
+ final FalseExit fe =
+ FalseExit.createFromContext(ctxt.getContext());
+ fe.fillInStackTrace();
+ throw fe;
+ } catch (final IOException ioe) {
+ Cldbm.error(ctxt, "Can't open/create file");
+ return Value.UNIT; // never reached
+ } catch (final Exception e) {
+ Cldbm.error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'open(CodeRunner, Value, Value, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value close(final CodeRunner ctxt,
+ final Value db)
+ throws Fail.Exception, FalseExit {
+ try {
+ get(ctxt, db).close();
+ } catch (final InterruptedIOException iioe) {
+ final FalseExit fe =
+ FalseExit.createFromContext(ctxt.getContext());
+ fe.fillInStackTrace();
+ db.asBlock().setCustom(null);
+ throw fe;
+ } catch (final IOException ioe) {
+ // nothing to do ...
+ } catch (final Fail.Exception fe) {
+ db.asBlock().setCustom(null);
+ throw fe;
+ } catch (final Exception e) {
+ // nothing to do ...
+ } // end try/catch
+ db.asBlock().setCustom(null);
+ return Value.UNIT;
+ } // end method 'close(CodeRunner, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value fetch(final CodeRunner ctxt,
+ final Value db,
+ final Value key)
+ throws Fail.Exception, FalseExit {
+ try {
+ final byte[] res =
+ get(ctxt, db).db.get(new Key(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 Fail.Exception fe) {
+ throw fe;
+ } catch (final Exception e) {
+ Cldbm.error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'fetch(CodeRunner, Value, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value insert(final CodeRunner ctxt,
+ final Value db,
+ final Value key,
+ final Value content)
+ throws Fail.Exception, FalseExit {
+ final Map database = get(ctxt, db).db;
+ try {
+ final Key k = new Key(key.asBlock().getBytes());
+ final byte[] res = database.get(k);
+ if (res != null) {
+ Cldbm.error(ctxt, "Entry already exists");
+ return Value.UNIT; // never reached
+ } else {
+ database.put(k, content.asBlock().getBytes());
+ return Value.UNIT;
+ } // end if/else
+ } catch (final Fail.Exception fe) {
+ throw fe;
+ } catch (final Exception e) {
+ Cldbm.error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'insert(CodeRunner, Value, Value, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value replace(final CodeRunner ctxt,
+ final Value db,
+ final Value key,
+ final Value content)
+ throws Fail.Exception, FalseExit {
+ final Map database = get(ctxt, db).db;
+ try {
+ database.put(new Key(key.asBlock().getBytes()),
+ content.asBlock().getBytes());
+ return Value.UNIT;
+ } catch (final Exception e) {
+ Cldbm.error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'replace(CodeRunner, Value, Value, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value delete(final CodeRunner ctxt,
+ final Value db,
+ final Value key)
+ throws Fail.Exception, FalseExit {
+ final Map database = get(ctxt, db).db;
+ try {
+ database.remove(new Key(key.asBlock().getBytes()));
+ return Value.UNIT;
+ } catch (final Exception e) {
+ Cldbm.error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'delete(CodeRunner, Value, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value firstkey(final CodeRunner ctxt,
+ final Value db)
+ throws Fail.Exception {
+ final DB database = get(ctxt, db);
+ // the iterator is taken from a copy of the actual set
+ // to avoid any concurrent modification exception
+ final Iterator it = new LinkedHashSet(database.db.keySet()).iterator();
+ database.iterator = it;
+ if (!it.hasNext()) {
+ Fail.raiseNotFound();
+ } // end if
+ return Value.createFromBlock(Block.createString(it.next().bytes));
+ } // end method 'firstkey(CodeRunner, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value nextkey(final CodeRunner ctxt,
+ final Value db)
+ throws Fail.Exception {
+ final Iterator it = get(ctxt, db).iterator;
+ if ((it == null) || !it.hasNext()) {
+ Fail.raiseNotFound();
+ } // end if
+ return Value.createFromBlock(Block.createString(it.next().bytes));
+ } // end method 'nextkey(CodeRunner, Value)'
+
+ /**
+ * Creates a Dbm.t instance from a file.
+ * @param f file to create instance from - should not be null
+ * @return a Dbm.t instance from a file
+ */
+ private static Value createDbm(final File f, final int flags) throws IOException {
+ assert f != null : "null f";
+ final Block res = Block.createBlock(1, Block.ABSTRACT_TAG);
+ res.setCustom(new DB(f, flags));
+ return Value.createFromBlock(res);
+ } // end method 'createDbm(File)'
+
+ /**
+ * Returns the {@link fr.x9c.cadmium.primitives.dbm.BasicImplementation.DB} object
+ * encapsulated inside a value.
+ * @param ctxt context - should not be null
+ * @param db value to get data object from
+ * - should not be null
+ * @return the {@link fr.x9c.cadmium.primitives.dbm.BasicImplementation.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 {
+ Cldbm.error(ctxt, "DBM has been closed");
+ return null; // never reached
+ } // end if/else
+ } // end method 'get(CodeRunner, Value)'
+
+ /**
+ * This inner-class implements a simple wrapper around a byte array.
+ * This wrapper is needed to have an object that provides
+ * correct implementations for hashCode()
+ * and equals(Object).
+ * Such methods are needed to use a byte array as a key value
+ * in a map.
+ */
+ private static final class Key {
+
+ /** Wrapped value. */
+ private final byte[] bytes;
+
+ /**
+ * Wraps a byte array.
+ * @param b byte array to wrap - should not be null
+ */
+ private Key(final byte[] b) {
+ assert b != null : "null b";
+ this.bytes = Arrays.copyOf(b, b.length);
+ } // end constructor (byte[])
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(this.bytes);
+ } // end method 'hashCode()'
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj instanceof Key) {
+ final Key that = (Key) obj;
+ return Arrays.equals(this.bytes, that.bytes);
+ } else {
+ return false;
+ } // end if/else
+ } // end method 'equals(Object)'
+
+ } // end inner-class 'Key'
+
+ /**
+ * This inner-class is a mere structure describing a database.
+ */
+ private static final class DB {
+
+ /** Actual database. */
+ private final Map db;
+
+ /** Actual file. */
+ private final File file;
+
+ /** Allowed database operations. */
+ private final int flags;
+
+ /** Current enumeration over keys. */
+ private Iterator iterator;
+
+ /**
+ * Constructs a structure from a database.
+ * Enumeration is set to null.
+ * @param d handle to database - should not be null
+ */
+ @SuppressWarnings("unchecked")
+ private DB(final File f, final int flags) throws IOException {
+ assert f != null : "null f";
+ if (((flags & READ) != 0) && f.exists()) {
+ final InputStream is = new FileInputStream(this.file);
+ final ObjectInputStream ois = new ObjectInputStream(is);
+ try {
+ this.db = (HashMap) ois.readObject();
+ } catch (final ClassNotFoundException cnfe) {
+ throw new IOException("invalid file format");
+ } catch (final ClassCastException cce) {
+ throw new IOException("invalid file format");
+ } // end try/catch
+ try {
+ ois.close();
+ } catch (final Exception e) {
+ // fail silently
+ } // end try/catch
+ try {
+ is.close();
+ } catch (final Exception e) {
+ // fail silently
+ } // end try/catch
+ } else {
+ this.db = new HashMap();
+ } // end if/else
+ this.file = f;
+ this.flags = flags;
+ this.iterator = null;
+ } // end constructor(File, int)
+
+ /**
+ * Closes the database, writing data if requested.
+ */
+ private void close() throws IOException {
+ if ((this.flags & WRITE) != 0) {
+ final OutputStream os = new FileOutputStream(this.file);
+ final ObjectOutputStream oos = new ObjectOutputStream(os);
+ oos.writeObject(this.db);
+ try {
+ oos.close();
+ } catch (final Exception e) {
+ // fail silently
+ } // end try/catch
+ try {
+ os.close();
+ } catch (final Exception e) {
+ // fail silently
+ } // end try/catch
+ } // end if
+ } // end method 'close()'
+
+ /**
+ * {@inheritDoc}
+ */
+ protected void finalize() {
+ try {
+ close();
+ } catch (final IOException ioe) {
+ // fail silently
+ } // try/catch
+ } // end method 'finalize()'
+
+ } // end inner-class 'DB'
+
+} // end class 'BasicImplementation'
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 21
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.util.Enumeration;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 24
-import org.w3c.tools.dbm.jdbm;
-
-import fr.x9c.cadmium.kernel.Block;
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 25
+import fr.x9c.cadmium.kernel.Context;
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 27
+import fr.x9c.cadmium.kernel.Fatal;
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 32
+import fr.x9c.cadmium.util.CustomClassLoader;
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 44
- /** Suffix for database files. */
- private static final String FILE_SUFFIX = ".db";
+ /** Slot identifier for the actual Dbm implementation. */
+ private static final Object SLOT = new Object();
+
+ /** Path to jdbm implementation stream. */
+ private static final String JDBM_STREAM = "/fr/x9c/cadmium/primitives/dbm/JdbmImplementation.clazz";
+
+ /** Fully-qualified class name for jdbm implementation. */
+ private static final String JDBM_CLASS = "fr.x9c.cadmium.primitives.dbm.JdbmImplementation";
+
+ /** Class for the jdbm-based implementation. */
+ private static Class jdbmClass = null;
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 66
- * @param flags ignored (O_RDWR | O_CREAT is used)
- * @param mode ignored
+ * @param flags allowed database operations
+ * @param mode file permissions for created file
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 71
+ * @throws Fatal.Exception if an implementation cannot be created
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 78
- 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
+ throws Fail.Exception, Fatal.Exception, FalseExit {
+ return getImpl(ctxt).open(ctxt, file, flags, mode);
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 89
+ * @throws Fatal.Exception if an implementation cannot be created
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 94
- 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;
+ throws Fail.Exception, Fatal.Exception, FalseExit {
+ return getImpl(ctxt).close(ctxt, db);
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 109
+ * @throws Fatal.Exception if an implementation cannot be created
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 115
- 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
+ throws Fail.Exception, Fatal.Exception, FalseExit {
+ return getImpl(ctxt).fetch(ctxt, db, key);
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 131
+ * @throws Fatal.Exception if an implementation cannot be created
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 138
- 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
+ throws Fail.Exception, Fatal.Exception, FalseExit {
+ return getImpl(ctxt).insert(ctxt, db, key, content);
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 153
+ * @throws Fatal.Exception if an implementation cannot be created
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 160
- 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
+ throws Fail.Exception, Fatal.Exception, FalseExit {
+ return getImpl(ctxt).replace(ctxt, db, key, content);
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 173
+ * @throws Fatal.Exception if an implementation cannot be created
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 179
- 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
+ throws Fail.Exception, Fatal.Exception, FalseExit {
+ return getImpl(ctxt).delete(ctxt, db, key);
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 192
+ * @throws Fatal.Exception if an implementation cannot be created
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 197
- 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()));
+ throws Fail.Exception, Fatal.Exception {
+ return getImpl(ctxt).firstkey(ctxt, db);
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 210
+ * @throws Fatal.Exception if an implementation cannot be created
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 215
- 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()));
+ throws Fail.Exception, Fatal.Exception {
+ return getImpl(ctxt).nextkey(ctxt, db);
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 220
- * 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.
+ * Returns the actual implementation for the Dbm module.
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 222
- * @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
+ * @return the actual implementation for the Dbm module
+ * @throws Fatal.Exception if an implementation cannot be created
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 225
- private static DB get(final CodeRunner ctxt,
- final Value db)
- throws Fail.Exception {
+ private static DbmImplementation getImpl(final CodeRunner ctxt)
+ throws Fatal.Exception {
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 228
- assert db != null : "null db";
- final DB res = (DB) db.asBlock().asCustom();
- if (res != null) {
- return res;
+ final Context c = ctxt.getContext();
+ final DbmImplementation impl = (DbmImplementation) c.getSlot(Cldbm.SLOT);
+ if (impl != null) {
+ return impl;
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 233
- error(ctxt, "DBM has been closed");
- return null; // never reached
+ final DbmImplementation newImpl = c.getParameters().isJDBMUsed()
+ ? newJdbmImplementation()
+ : new BasicImplementation();
+ c.registerSlot(Cldbm.SLOT, newImpl);
+ return newImpl;
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 239
- } // end method 'get(CodeRunner, Value)'
+ } // end method 'getImpl(CodeRunner)'
+
+ /**
+ * Creates a JDBM-based implementation using reflection.
+ * @return a new JDBM-based implementation
+ * @throws Fail.Exception if an implementation cannot be created
+ */
+ private static DbmImplementation newJdbmImplementation()
+ throws Fatal.Exception {
+ synchronized (Cldbm.class) {
+ if (Cldbm.jdbmClass == null) {
+ try {
+ final InputStream is = Cldbm.class.getResourceAsStream(Cldbm.JDBM_STREAM);
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int b = is.read();
+ while (b != -1) {
+ baos.write(b);
+ b = is.read();
+ } // end while
+ Cldbm.jdbmClass =
+ CustomClassLoader.INSTANCE.defineAndResolve(Cldbm.JDBM_CLASS,
+ baos.toByteArray());
+ } catch (final Throwable t) {
+ Fatal.raise("Cannot create a JDBM implementation");
+ return null; // never reached
+ } // end try/catch
+ } // end if
+ } // end synchronized
+ if (Cldbm.jdbmClass == null) {
+ Fatal.raise("Cannot create a JDBM implementation");
+ } // end if
+ try {
+ return (DbmImplementation) Cldbm.jdbmClass.newInstance();
+ } catch (final InstantiationException ie) {
+ Fatal.raise("Cannot create a JDBM implementation");
+ return null; // never reached
+ } catch (final IllegalAccessException iae) {
+ Fatal.raise("Cannot create a JDBM implementation");
+ return null; // never reached
+ } // end try/catch
+ } // end method 'newJdbmImplementation()'
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 287
- private static void error(final CodeRunner ctxt,
- final String msg)
+ public static void error(final CodeRunner ctxt,
+ final String msg)
hunk ./src/fr/x9c/cadmium/primitives/dbm/Cldbm.java 300
- /**
- * 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'
-
addfile ./src/fr/x9c/cadmium/primitives/dbm/DbmImplementation.java
hunk ./src/fr/x9c/cadmium/primitives/dbm/DbmImplementation.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 fr.x9c.cadmium.kernel.CodeRunner;
+import fr.x9c.cadmium.kernel.Fail;
+import fr.x9c.cadmium.kernel.FalseExit;
+import fr.x9c.cadmium.kernel.Value;
+
+/**
+ * This interface defines a Dbm implementation.
+ *
+ * @author Xavier Clerc
+ * @version 1.1
+ * @since 1.1
+ */
+public interface DbmImplementation {
+
+ /**
+ * Opens a database.
+ * @param ctxt context
+ * @param file database file
+ * @param flags allowed database operations
+ * @param mode file permissions for created file
+ * @return database handle
+ * @throws Fail.Exception Dbm.Dbm_error if the file cannot be
+ * opened/created
+ */
+ Value open(CodeRunner ctxt, Value file, Value flags, Value mode)
+ throws Fail.Exception, FalseExit;
+
+ /**
+ * Closes a database.
+ * @param ctxt context
+ * @param db database handle
+ * @return unit
+ * @throws Fail.Exception Dbm.Dbm_error if the database is already
+ * closed
+ */
+ Value close(CodeRunner ctxt, Value db)
+ throws Fail.Exception, FalseExit;
+
+ /**
+ * 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
+ */
+ Value fetch(CodeRunner ctxt, Value db, Value key)
+ throws Fail.Exception, FalseExit;
+
+ /**
+ * 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
+ */
+ Value insert(CodeRunner ctxt, Value db, Value key, Value content)
+ throws Fail.Exception, FalseExit;
+
+ /**
+ * 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
+ */
+ Value replace(CodeRunner ctxt, Value db, Value key, Value content)
+ throws Fail.Exception, FalseExit;
+
+ /**
+ * 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
+ */
+ Value delete(CodeRunner ctxt, Value db, Value key)
+ throws Fail.Exception, FalseExit;
+
+ /**
+ * 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
+ */
+ Value firstkey(CodeRunner ctxt, Value db)
+ throws Fail.Exception;
+
+ /**
+ * 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
+ */
+ Value nextkey(CodeRunner ctxt, Value db)
+ throws Fail.Exception;
+
+} // end interface 'DbmImplementation'
addfile ./src/fr/x9c/cadmium/primitives/dbm/JdbmImplementation.java
hunk ./src/fr/x9c/cadmium/primitives/dbm/JdbmImplementation.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.File;
+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.Value;
+
+/**
+ * This interface defines a Dbm implementation based on JDBM.
+ * This class will be dynamically loaded by a class loader in order to get rid
+ * of the dependency to org.w3c.tools.dbm.jdbm. More, the load will
+ * occur only if explicitly requested by the user (the default is to use a
+ * pure-JDK implementation for Dbm).
+ *
+ * @author Xavier Clerc
+ * @version 1.1
+ * @since 1.1
+ */
+public final class JdbmImplementation implements DbmImplementation {
+
+ /** Suffix for database files. */
+ private static final String FILE_SUFFIX = ".db";
+
+ /**
+ * Empty constructor.
+ */
+ public JdbmImplementation() {
+ } // end empty constructor
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value open(final CodeRunner ctxt,
+ final Value file,
+ final Value flags,
+ final Value mode)
+ throws Fail.Exception, FalseExit {
+ try {
+ final File f = ctxt.getContext().getRealFile(file.asBlock().asString()
+ + JdbmImplementation.FILE_SUFFIX);
+ return createDbm(new jdbm(f.getAbsolutePath()));
+ } catch (final InterruptedIOException iioe) {
+ final FalseExit fe =
+ FalseExit.createFromContext(ctxt.getContext());
+ fe.fillInStackTrace();
+ throw fe;
+ } catch (final IOException ioe) {
+ Cldbm.error(ctxt, "Can't open/create file");
+ return Value.UNIT; // never reached
+ } catch (final Exception e) {
+ Cldbm.error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'open(CodeRunner, Value, Value, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value 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();
+ db.asBlock().setCustom(null);
+ throw fe;
+ } catch (final IOException ioe) {
+ // nothing to do ...
+ } catch (final Fail.Exception fe) {
+ db.asBlock().setCustom(null);
+ throw fe;
+ } catch (final Exception e) {
+ // nothing to do ...
+ } // end try/catch
+ db.asBlock().setCustom(null);
+ return Value.UNIT;
+ } // end method 'close(CodeRunner, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value 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) {
+ Cldbm.error(ctxt, "dbm_fetch failed");
+ return Value.UNIT; // never reached
+ } catch (final Fail.Exception fe) {
+ throw fe;
+ } catch (final Exception e) {
+ Cldbm.error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'fetch(CodeRunner, Value, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value 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) {
+ Cldbm.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) {
+ Cldbm.error(ctxt, "dbm_store failed");
+ return Value.UNIT; // never reached
+ } catch (final Fail.Exception fe) {
+ throw fe;
+ } catch (final Exception e) {
+ Cldbm.error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'insert(CodeRunner, Value, Value, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value 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) {
+ Cldbm.error(ctxt, "dbm_store failed");
+ return Value.UNIT; // never reached
+ } catch (final Exception e) {
+ Cldbm.error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'replace(CodeRunner, Value, Value, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value 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) {
+ Cldbm.error(ctxt, "dbm_delete failed");
+ return Value.UNIT; // never reached
+ } catch (final Exception e) {
+ Cldbm.error(ctxt, e.toString());
+ return Value.UNIT; // never reached
+ } // end try/catch
+ } // end method 'delete(CodeRunner, Value, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value firstkey(final CodeRunner ctxt,
+ final Value db)
+ throws Fail.Exception {
+ final JdbmImplementationDB 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 'firstkey(CodeRunner, Value)'
+
+ /**
+ * {@inheritDoc}
+ */
+ public Value 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 'nextkey(CodeRunner, Value)'
+
+ /**
+ * Creates a Dbm.t instance by encapsulating a
+ * {@link fr.x9c.cadmium.primitives.dbm.JdbmImplementationDB} 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 JdbmImplementationDB(db));
+ return Value.createFromBlock(res);
+ } // end method 'createDbm(jdbm)'
+
+ /**
+ * Returns the {@link fr.x9c.cadmium.primitives.dbm.JdbmImplementationDB} 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.JdbmImplementationDB} object
+ * encapsulated inside a value
+ * @throws Fail.Exception if db has been closed
+ */
+ @SuppressWarnings("unchecked")
+ private static JdbmImplementationDB get(final CodeRunner ctxt,
+ final Value db)
+ throws Fail.Exception {
+ assert ctxt != null : "null ctxt";
+ assert db != null : "null db";
+ final JdbmImplementationDB res = (JdbmImplementationDB) db.asBlock().asCustom();
+ if (res != null) {
+ return res;
+ } else {
+ Cldbm.error(ctxt, "DBM has been closed");
+ return null; // never reached
+ } // end if/else
+ } // end method 'get(CodeRunner, Value)'
+
+} // end class 'JdbmImplementation'
addfile ./src/fr/x9c/cadmium/primitives/dbm/JdbmImplementationDB.java
hunk ./src/fr/x9c/cadmium/primitives/dbm/JdbmImplementationDB.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.util.Enumeration;
+
+/**
+ * This class is a mere structure describing a (database, enumeration) pair.
+ * In a standard setting, this class would be an inner-class of
+ * fr.x9c.cadmium.primitives.dbm.JdbmImplementation. However, due to the
+ * necessity of dynamic loading of fr.x9c.cadmium.primitives.dbm.JdbmImplementation,
+ * this class has been made standalone (and public) to avoid unnecessary juggling
+ * with class loaders.
+ *
+ * @author Xavier Clerc
+ * @version 1.1
+ * @since 1.1
+ */
+public final class JdbmImplementationDB {
+
+ /** Actual database. */
+ public final T db;
+
+ /** Current enumeration over keys. */
+ public Enumeration enumeration;
+
+ /**
+ * Constructs a structure from a database.
+ * Enumeration is set to null.
+ * @param d handle to database - should not be null
+ */
+ public JdbmImplementationDB(final T d) {
+ assert d != null : "null d";
+ this.db = d;
+ this.enumeration = null;
+ } // end constructor(T)
+
+} // end class 'JdbmImplementationDB'
hunk ./src/fr/x9c/cadmium/primitives/dbm/package.html 2
-This package contains the classes providing implementation for 'dbm' primitives.
+This package contains the classes providing implementations for 'dbm' primitives.
+Two implementations are provided: a simple one (based on collection classes) and
+an elaborated one based on jdbm.
hunk ./src/fr/x9c/cadmium/primitives/dbm/package.html 6
-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.
+These implementations do not guarantee that produced 'dot db' files are
+compatible with the reference implementation.
+
+The jdbm implementation is based on the 'jdbm' part of the Jigsaw project.
hunk ./src/fr/x9c/cadmium/primitives/dbm/package.html 17
-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.
+These implementations are provided for compatibility purpose only and a Cadmium
+user is strongly encouraged to use the CadmiumJDBC module, and
+Nickel in order to access to the full power of
+JDBC.
}