001: /*=============================================================================
002: * Copyright Texas Instruments 2000-2004. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * $ProjectHeader: OSCRIPT 0.155 Fri, 20 Dec 2002 18:34:22 -0800 rclark $
019: */
020:
021: package oscript.data;
022:
023: import oscript.exceptions.*;
024: import oscript.fs.*;
025: import oscript.*;
026:
027: import java.util.Hashtable;
028: import java.io.*;
029:
030: /**
031: * A database provides persistant storage.... for example
032: * <pre>
033: * var cache = new Database("cache.db");
034: * cache.foo.bar = "something";
035: * </pre>
036: *
037: * @author Rob Clark (rob@ti.com)
038: */
039: public class Database extends OObject {
040: private Hashtable hashtable; // key is String to make serialize easier...
041: private Object obj;
042: private AbstractFile file;
043: private boolean sideEffects;
044: private boolean needsFlush = true;
045:
046: /**
047: * The type object for an instance of Object.
048: */
049: public final static Value TYPE = BuiltinType
050: .makeBuiltinType("oscript.data.Database");
051: public final static String PARENT_TYPE_NAME = "oscript.data.OObject";
052: public final static String TYPE_NAME = "Database";
053: public final static String[] MEMBER_NAMES = new String[] {
054: "getType", "castToJavaObject", "castToString",
055: "bopInstanceOf", "bopEquals", "bopNotEquals", "getMember",
056: "get", "put", "flush" };
057:
058: /*=======================================================================*/
059:
060: /**
061: * Class Constructor.
062: *
063: * @param file the file to read from and store to
064: */
065: public Database(AbstractFile file) {
066: this (file, null, true);
067: }
068:
069: /**
070: * Class Constructor.
071: *
072: * @param file the file to read from and store to
073: * @param sideEffects can be set to <code>false</code> if you do not
074: * expect side effects (ie. member of database is not mutable). If
075: * <code>true</code>, the database is always flushed at exit,
076: * regardless of whether there have been any put() operations.
077: */
078: public Database(AbstractFile file, boolean sideEffects) {
079: this (file, null, true);
080: }
081:
082: /**
083: * Class Constructor.
084: *
085: * @param file the file to read from and store to
086: * @param loader an optional class loader used to resolve
087: * class references when loading the database from disk
088: */
089: public Database(AbstractFile file, final ClassLoader loader) {
090: this (file, loader, true);
091: }
092:
093: /**
094: * Class Constructor.
095: *
096: * @param file the file to read from and store to
097: * @param loader an optional class loader used to resolve
098: * class references when loading the database from disk
099: * @param sideEffects can be set to <code>false</code> if you do not
100: * expect side effects (ie. member of database is not mutable). If
101: * <code>true</code>, the database is always flushed at exit,
102: * regardless of whether there have been any put() operations.
103: */
104: public Database(AbstractFile file, final ClassLoader loader,
105: boolean sideEffects) {
106: super ();
107:
108: this .file = file;
109: this .sideEffects = sideEffects;
110:
111: OscriptBuiltins.atExit(new Runnable() {
112: public void run() {
113: flush();
114: }
115: }, 10);
116:
117: if (file.canRead()) {
118: try {
119: ObjectInputStream ois = new ObjectInputStream(
120: new BufferedInputStream(file.getInputStream())) {
121:
122: protected Class resolveClass(ObjectStreamClass v)
123: throws IOException, ClassNotFoundException {
124: if (loader == null)
125: return super .resolveClass(v);
126: else
127: return Class.forName(v.getName(), false,
128: loader);
129: }
130:
131: };
132:
133: hashtable = (Hashtable) (ois.readObject());
134: } catch (InvalidClassException e) {
135: hashtable = new Hashtable();
136: } catch (IOException e) {
137: if (DEBUG)
138: e.printStackTrace();
139: } catch (Throwable e) {
140: if (DEBUG)
141: e.printStackTrace();
142:
143: throw OJavaException.makeJavaExceptionWrapper(e);
144: }
145: }
146:
147: if (hashtable == null)
148: hashtable = new Hashtable();
149: }
150:
151: /*=======================================================================*/
152: /**
153: * Class Constructor. This is the constructor that gets called via an
154: * BuiltinType instance.
155: *
156: * @param args arguments to this constructor
157: * @throws PackagedScriptObjectException(Exception) if wrong number of args
158: */
159: public Database(oscript.util.MemberTable args) {
160: this (argsToFile(args));
161: }
162:
163: private final static AbstractFile argsToFile(
164: oscript.util.MemberTable args) {
165: if (args.length() != 1)
166: throw PackagedScriptObjectException
167: .makeExceptionWrapper(new OIllegalArgumentException(
168: "wrong number of args!"));
169:
170: try {
171: if (args.referenceAt(0).unhand() instanceof AbstractFile)
172: return (AbstractFile) (args.referenceAt(0).unhand());
173: else
174: return OscriptInterpreter.resolve(args.referenceAt(0)
175: .castToString(), false);
176: } catch (Exception e) {
177: throw OJavaException.makeJavaExceptionWrapper(e);
178: }
179: }
180:
181: /*=======================================================================*/
182: /**
183: * Make sure we flush before we're gc'd.
184: */
185: protected void finalize() {
186: flush();
187: }
188:
189: /*=======================================================================*/
190: /**
191: * Get the type of this object. The returned type doesn't have to take
192: * into account the possibility of a script type extending a built-in
193: * type, since that is handled by {@link #getType}.
194: *
195: * @return the object's type
196: */
197: protected Value getTypeImpl() {
198: return TYPE;
199: }
200:
201: /*=======================================================================*/
202: /**
203: * Flush contents to file.
204: */
205: public synchronized void flush() {
206: if (needsFlush || sideEffects) {
207: try {
208: if (!file.exists())
209: file.createNewFile();
210:
211: ObjectOutputStream oos = new ObjectOutputStream(
212: new BufferedOutputStream(file
213: .getOutputStream(false)));
214:
215: oos.writeObject(hashtable);
216: oos.flush();
217: oos.close();
218: } catch (Throwable e) {
219: e.printStackTrace();
220: }
221:
222: needsFlush = false;
223: }
224: }
225:
226: // private static long totalWriteTime = 0;
227: // private static long totalReadTime = 0;
228:
229: /*=======================================================================*/
230: /**
231: * Get an entry from the database. The <code>key</code> is a "." seperated
232: * path, for example "foo.bar".
233: *
234: * @param key the key
235: * @return the value, or <code>null</code> if none.
236: */
237: public Value get(Value key) {
238: return get(key.castToString());
239: }
240:
241: public Value get(String key) {
242: return (Value) (hashtable.get(key));
243: }
244:
245: /*=======================================================================*/
246: /**
247: * Put an entry into the database. The <code>key</code> is a "." seperated
248: * path, for example "foo.bar".
249: *
250: * @param key the key
251: * @param val the new value
252: */
253: public void put(Value key, Value val) {
254: put(key.castToString(), val);
255: }
256:
257: public synchronized void put(String key, Value val) {
258: needsFlush = true;
259: hashtable.put(key, val);
260: }
261:
262: /*=======================================================================*/
263: public java.util.Iterator keys() {
264: return hashtable.keySet().iterator();
265: }
266:
267: public void remove(Value key) {
268: remove(key.castToString());
269: }
270:
271: public void remove(String key) {
272: hashtable.remove(key);
273: }
274:
275: /*=======================================================================*/
276: /**
277: * Get a member of this object.
278: *
279: * @param id the id of the symbol that maps to the member
280: * @param exception whether an exception should be thrown if the
281: * member object is not resolved
282: * @return a reference to the member
283: * @throws PackagedScriptObjectException(NoSuchMethodException)
284: * @throws PackagedScriptObjectException(NoSuchMemberException)
285: */
286: public Value getMember(final int id, boolean exception)
287: throws PackagedScriptObjectException {
288: Value member = super .getMember(id, false);
289:
290: if (member != null)
291: return member;
292:
293: final String name = Symbol.getSymbol(id).castToString();
294:
295: return new AbstractReference() {
296:
297: public void opAssign(Value val)
298: throws PackagedScriptObjectException {
299: val = val.unhand();
300: if (val != NULL)
301: put(name, val);
302: else
303: remove(name);
304: flush();
305: }
306:
307: protected Value get() {
308: Value val = Database.this .get(name);
309: if (val == null)
310: val = NULL;
311: return val;
312: }
313:
314: };
315: }
316:
317: /*=======================================================================*/
318: /**
319: * Derived classes that implement {@link #getMember} should also
320: * implement this.
321: *
322: * @param s the set to populate
323: * @param debugger <code>true</code> if being used by debugger, in
324: * which case both public and private/protected field names should
325: * be returned
326: * @see #getMember
327: */
328: protected void populateMemberSet(java.util.Set s, boolean debugger) {
329: for (java.util.Iterator itr = keys(); itr.hasNext();)
330: s.add(Symbol.getSymbol((String) (itr.next())));
331: }
332: }
333:
334: /*
335: * Local Variables:
336: * tab-width: 2
337: * indent-tabs-mode: nil
338: * mode: java
339: * c-indentation-style: java
340: * c-basic-offset: 2
341: * eval: (c-set-offset 'substatement-open '0)
342: * eval: (c-set-offset 'case-label '+)
343: * eval: (c-set-offset 'inclass '+)
344: * eval: (c-set-offset 'inline-open '0)
345: * End:
346: */
|