diff options
author | Burak Emir <emir@epfl.ch> | 2007-09-17 15:21:46 +0000 |
---|---|---|
committer | Burak Emir <emir@epfl.ch> | 2007-09-17 15:21:46 +0000 |
commit | 3f9b82c88d74c1b03daf5131b50c172213f40a63 (patch) | |
tree | cc2e9da177d9733f5e6eedc75666140feb9fcf68 /src | |
parent | 1c1b5e88fbd2076e64a59d04bbac6823a823bfd0 (diff) | |
download | scala-3f9b82c88d74c1b03daf5131b50c172213f40a63.tar.gz scala-3f9b82c88d74c1b03daf5131b50c172213f40a63.tar.bz2 scala-3f9b82c88d74c1b03daf5131b50c172213f40a63.zip |
added classes for persistent storage of XML
Diffstat (limited to 'src')
-rw-r--r-- | src/library/scala/xml/persistent/CachedFileStorage.scala | 109 | ||||
-rw-r--r-- | src/library/scala/xml/persistent/Index.scala | 5 | ||||
-rw-r--r-- | src/library/scala/xml/persistent/IndexedStorage.scala | 37 | ||||
-rw-r--r-- | src/library/scala/xml/persistent/SetStorage.scala | 30 |
4 files changed, 181 insertions, 0 deletions
diff --git a/src/library/scala/xml/persistent/CachedFileStorage.scala b/src/library/scala/xml/persistent/CachedFileStorage.scala new file mode 100644 index 0000000000..32d74a7117 --- /dev/null +++ b/src/library/scala/xml/persistent/CachedFileStorage.scala @@ -0,0 +1,109 @@ +package scala.xml.persistent + +import java.io.{File,FileOutputStream} +import java.nio.ByteBuffer +import java.nio.channels.Channels + +/** mutable storage of immutable xml trees. Everything is kept in memory, + * with a thread periodically checking for changes and writing to file. + * To ensure atomicity, two files are used, filename1 and '$'+filename1. + * The implementation switches between the two, deleting the older one + * after a complete dump of the database has been written. + */ +abstract class CachedFileStorage(private val file1: File) +extends java.lang.Thread with scala.util.logging.Logged { + + private val file2 = new File(file1.getParent, file1.getName+"$") + + /** either equals file1 or file2, references the next file in which updates will be stored + */ + private var theFile: File = null + + private def switch = { theFile = if( theFile == file1 ) file2 else file1; } + + /** this storage modified since last modification check */ + protected var dirty = false + + /** period between modification checks, in milliseconds */ + protected val interval = 1000 + + /** finds and loads the storage file. subclasses should call this method + * prior to any other, but only once, to obtain the initial sequence of nodes. + */ + protected def initialNodes: Iterator[Node] = (file1.exists, file2.exists) match { + case (false,false) => + theFile = file1 + Iterator.empty + case (true, true ) if (file1.lastModified < file2.lastModified) => + theFile = file2 + load + case (true, _ ) => + theFile = file1 + load + case _ => + theFile = file2 + load + } + + /** returns an iterator over the nodes in this storage */ + def nodes: Iterator[Node] + + /** adds a node, setting this.dirty to true as a side effect */ + def += ( e:Node ): Unit + + /** removes a tree, setting this.dirty to true as a side effect */ + def -= ( e:Node ): Unit + + /* loads and parses XML from file */ + private def load: Iterator[Node] = { + import scala.io.Source + import scala.xml.parsing.ConstructingParser + log("[load]\nloading "+theFile) + val src = Source.fromFile( theFile ) + log("parsing "+theFile) + val res = ConstructingParser.fromSource(src,false).document.docElem(0) + switch + log("[load done]") + res.child.elements + } + + /** saves the XML to file */ + private def save = if( this.dirty ) { + log("[save]\ndeleting "+theFile); + theFile.delete(); + log("creating new "+theFile); + theFile.createNewFile(); + val fos = new FileOutputStream( theFile ); + val c = fos.getChannel(); + + // @todo: optimize + val storageNode = <nodes>{ nodes.toList }</nodes> + val w = Channels.newWriter(c, "utf-8") + XML.write(w, storageNode, "utf-8", true, null) + + log("writing to "+theFile); + + w.close + c.close + fos.close + dirty = false + switch + log("[save done]") + } + + /** run method of the thread. remember to use start() to start a thread, not run. */ + override def run = { + log("[run]\nstarting storage thread, checking every "+interval+" ms"); + while(true) { + Thread.sleep( this.interval ); + save + } + } + + /** forces writing of contents to the file, even if there has not been any update. */ + def flush = { + this.dirty = true; + save + } +} + diff --git a/src/library/scala/xml/persistent/Index.scala b/src/library/scala/xml/persistent/Index.scala new file mode 100644 index 0000000000..cf7334c5f4 --- /dev/null +++ b/src/library/scala/xml/persistent/Index.scala @@ -0,0 +1,5 @@ +package scala.xml.persistent + +/** an Index returns some unique key that is part of a node + */ +abstract class Index[A] extends Function1[Node,A] {} diff --git a/src/library/scala/xml/persistent/IndexedStorage.scala b/src/library/scala/xml/persistent/IndexedStorage.scala new file mode 100644 index 0000000000..5af307a210 --- /dev/null +++ b/src/library/scala/xml/persistent/IndexedStorage.scala @@ -0,0 +1,37 @@ +package scala.xml.persistent + +import scala.collection.mutable +import java.io.File + +/** indexed multiset of xml trees. The index may be an arbitrary totally + * type, especially one can construct indices by selecting parts of + * xml nodes. + */ +class IndexedStorage[A](file: File, index: Index[A]) //@todo +extends CachedFileStorage( file ) { + + private var theMap: mutable.Map[A,Node] = new mutable.HashMap[A,Node]() + + super.initialNodes.foreach { x:Node => this += x } + + this.dirty = false + + def += (e: Node): Unit = synchronized { + log("added element at index '"+index(e)+"'") + dirty = true + theMap(index(e)) = e + } + + def -= (e: Node): Unit = synchronized { + log("removed element at index '"+index(e)+"'") + dirty = true + theMap -= index( e ) + } + + def nodes: Iterator[Node] = synchronized { + theMap.values + } + + def lookup(n: A): Option[Node] = theMap.get(n) + +} diff --git a/src/library/scala/xml/persistent/SetStorage.scala b/src/library/scala/xml/persistent/SetStorage.scala new file mode 100644 index 0000000000..d094e6514a --- /dev/null +++ b/src/library/scala/xml/persistent/SetStorage.scala @@ -0,0 +1,30 @@ +package scala.xml.persistent + +import scala.collection.mutable +import java.io.File +/** a persistent store with set semantics. This class allows to add and remove + * trees, but never contains two structurally equal trees. + */ +class SetStorage(file: File) extends CachedFileStorage(file) { + + private var theSet: mutable.HashSet[Node] = new mutable.HashSet[Node] + + // initialize + + { + val it = super.initialNodes + dirty = it.hasNext + for(val x <- it) { + theSet += x; + } + } + + /* forwarding methods to hashset*/ + + def += ( e:Node ): Unit = synchronized { this.dirty = true; theSet += e } + + def -= ( e:Node ): Unit = synchronized { this.dirty = true; theSet -= e } + + def nodes = synchronized { theSet.elements } + +} |