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