diff --git a/.idea/modules.xml b/.idea/modules.xml
index 36ef0191ae1..c1f4844a4d1 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -7,6 +7,7 @@
+
diff --git a/examples/example-vfs/example-vfs.iml b/examples/example-vfs/example-vfs.iml
new file mode 100644
index 00000000000..37bed8abc1d
--- /dev/null
+++ b/examples/example-vfs/example-vfs.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/example-vfs/example-vfs.ipr b/examples/example-vfs/example-vfs.ipr
new file mode 100644
index 00000000000..77f63b1e9c3
--- /dev/null
+++ b/examples/example-vfs/example-vfs.ipr
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+ http://www.w3.org/1999/xhtml
+
+
+
+
+
+
+
+
+
diff --git a/examples/example-vfs/src/FileSystem.kt b/examples/example-vfs/src/FileSystem.kt
new file mode 100644
index 00000000000..e9380227794
--- /dev/null
+++ b/examples/example-vfs/src/FileSystem.kt
@@ -0,0 +1,130 @@
+package org.jetbrains.jet.samples.vfs;
+
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.jetbrains.jet.samples.vfs.utils.*;
+import java.util.concurrent.locks.Lock
+import java.io.File
+import java.util.List
+import java.util.ArrayList
+import java.util.HashMap
+import java.util.Arrays
+import java.util.Map
+
+import std.util.*
+
+/**
+ * File system singleton. To work with virtual file system, read/write locks should be
+ * acquired: use read() and write() higher-order functions.
+ */
+public object FileSystem {
+ private val lock = ReentrantReadWriteLock()
+ internal val watchedDirectories = ArrayList
+
+ // FIXME VirtualFiles should be used as hashmap keys themselves,
+ // but overriden hashCode() method fails in runtime with ClassCastException (KT-1134)
+ /**
+ * Mapping from virtual files to metainformation
+ */
+ internal val fileToInfo = HashMap()
+ private val listeners = ArrayList()
+
+ /**
+ * Returns corresponding virtual file for java.io.File.
+ *
+ * @param ioFile file
+ */
+ public fun getFileByIoFile(ioFile : File) : VirtualFile {
+ FileSystem.assertCanRead()
+ return PhysicalVirtualFile(ioFile.getAbsolutePath().sure().toSystemIndependentPath())
+ }
+
+ /**
+ * Returns virtual file for path to it.
+ *
+ * @param path path to file
+ */
+ public fun getFileByPath(path : String) : VirtualFile {
+ return getFileByIoFile(File(path))
+ }
+
+ /**
+ * Runs function with read lock.
+ */
+ public inline fun read(task : () -> T) : T {
+ return locked(lock.readLock().sure(), task)
+ }
+
+ /**
+ * Runs function with write lock.
+ */
+ public inline fun write(task : () -> T) : T {
+ return locked(lock.writeLock().sure(), task)
+ }
+
+ /**
+ * Adds directory to list of watched directories. Watched directories are
+ * periodically refreshed and corresponding events are sent to file system listeners.
+ *
+ * @param dir directory which should be watched
+ */
+ public fun addWatchedDirectory(dir : VirtualFile) {
+ assertCanRead()
+ if (dir.isDirectory()) {
+ watchedDirectories.add(dir)
+ scanAndAddRecursivelyNoEvents(dir)
+
+ RefreshQueue.scheduleRefresh()
+ }
+ }
+
+ /* Scans file recursively and adds info about it to fileToInfo map */
+ private fun scanAndAddRecursivelyNoEvents(file : VirtualFile) {
+ assert(FileSystem.fileToInfo[file.path] == null)
+
+ val fileInfo = VirtualFileInfo(file)
+ FileSystem.fileToInfo[file.path] = fileInfo
+ fileInfo.children.foreach{ scanAndAddRecursivelyNoEvents(it) }
+ }
+
+ internal inline fun assertCanRead() {
+ assert(lock.getReadHoldCount() != 0 || lock.isWriteLockedByCurrentThread())
+ }
+
+ internal inline fun assertCanWrite() {
+ assert(lock.isWriteLockedByCurrentThread())
+ }
+
+ /**
+ * Adds file system listener which should be notified about changing of file system.
+ */
+ public fun addVirtualFileListener(listener : VirtualFileListener) {
+ listeners.add(listener)
+ }
+
+ /**
+ * Removes file system listener.
+ */
+ public fun removeVirtualFileListener(listener : VirtualFileListener) {
+ listeners.remove(listener)
+ }
+
+ /* Notifies all listeners */
+ internal fun notifyEventHappened(event : VirtualFileEvent) {
+ for (listener in listeners) {
+ listener.eventHappened(event)
+ }
+ }
+}
+
+private class VirtualFileInfo(file : VirtualFile) {
+ /* Last modification time */
+ var lastModified : Long = 0
+ /* List of known children */
+ val children : List = ArrayList;
+
+ {
+ children.addAll(file.children())
+ lastModified = file.modificationTime()
+ }
+}
diff --git a/examples/example-vfs/src/Main.kt b/examples/example-vfs/src/Main.kt
new file mode 100644
index 00000000000..7c288a228b2
--- /dev/null
+++ b/examples/example-vfs/src/Main.kt
@@ -0,0 +1,36 @@
+package org.jetbrains.jet.samples.vfs.sandbox;
+
+import org.jetbrains.jet.samples.vfs.*;
+import java.io.InputStreamReader
+import java.util.Scanner
+
+/**
+ * Receives list of directories which should be watched, adds them to virtual file system,
+ * adds virtual file system listener and waits for 1 minutes, printing out all received event.
+ */
+fun main(args : Array) {
+ if (args.size == 0) {
+ println("Provide list of watched directories as command line arguments")
+ return
+ }
+
+ // add watched directory
+ FileSystem.write {
+ for (val arg in args) {
+ FileSystem.addWatchedDirectory(FileSystem.getFileByPath(arg))
+ }
+ }
+
+ // add listener which prints out everything
+ FileSystem.addVirtualFileListener(object : VirtualFileListener {
+ override fun eventHappened(event: VirtualFileEvent) {
+ println(event)
+ if (event is VirtualFileChangedEvent) {
+ println("new file size is ${event.file.size()}")
+ }
+ }
+ })
+
+ // wait for 1 minute
+ Thread.sleep(60000)
+}
\ No newline at end of file
diff --git a/examples/example-vfs/src/RefreshQueue.kt b/examples/example-vfs/src/RefreshQueue.kt
new file mode 100644
index 00000000000..09e7f21713f
--- /dev/null
+++ b/examples/example-vfs/src/RefreshQueue.kt
@@ -0,0 +1,138 @@
+package org.jetbrains.jet.samples.vfs;
+
+import java.util.concurrent.ConcurrentLinkedQueue
+import java.util.List
+import java.util.concurrent.locks.ReentrantLock
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.ArrayList
+
+import std.util.*
+import java.util.Timer
+import java.util.TimerTask
+
+import org.jetbrains.jet.samples.vfs.utils.*
+
+/**
+ * Singleton which creates thread for periodically checking if there are changes in
+ * file system and notifying file system listeners.
+ */
+internal object RefreshQueue : Runnable {
+ private val taskQueue = LinkedBlockingQueue>()
+ private val workerThread: Thread = Thread(this)
+ private val fullRefreshScheduler = Timer(true);
+
+ {
+ workerThread.setDaemon(true)
+ workerThread.start()
+
+ fullRefreshScheduler.scheduleAtFixedRate(createTimerTask {
+ scheduleRefresh(FileSystem.watchedDirectories)
+ }, 0, 5000)
+ }
+
+ private fun takeAndRefreshFiles() {
+ refreshFiles(taskQueue.take())
+ }
+
+ /* Acquires write lock and refreshes file system */
+ private fun refreshFiles(files : List) {
+ FileSystem.write {
+ files.foreach{ refreshFile(it) }
+ }
+ }
+
+ /* Checks for changes in virtual file recursively, notifying listeners. */
+ private fun refreshFile(file : VirtualFile) {
+ FileSystem.assertCanWrite()
+ if (file.isDirectory()) {
+ val fileToInfo = FileSystem.fileToInfo
+ val fileInfo = fileToInfo[file.path]
+ val oldChildren = fileInfo.children
+ val newChildren = file.children()
+
+ val addedChildren = listDifference(newChildren, oldChildren)
+ val deletedChildren = listDifference(oldChildren, newChildren)
+ val commonChildren = listIntersection(oldChildren, newChildren)
+
+ addedChildren.foreach{ addRecursively(it) }
+ deletedChildren.foreach{ deleteRecursively(it) }
+
+ fileInfo.children.clear()
+ fileInfo.children.addAll(newChildren)
+
+ commonChildren.foreach{ refreshFile(it) }
+ } else {
+ val fileInfo = FileSystem.fileToInfo[file.path]
+ assert(fileInfo != null)
+
+ val newModificationTime = file.modificationTime()
+ if (fileInfo.lastModified != newModificationTime) {
+ fileInfo.lastModified = newModificationTime
+
+ FileSystem.notifyEventHappened(VirtualFileChangedEvent(file))
+ }
+ }
+ }
+
+ /* Adds file to file system recursively, notifying listeners */
+ private fun addRecursively(file : VirtualFile) {
+ assert(FileSystem.fileToInfo[file] == null)
+
+ val fileInfo = VirtualFileInfo(file)
+
+ FileSystem.fileToInfo[file.path] = fileInfo
+ FileSystem.notifyEventHappened(VirtualFileCreateEvent(file))
+
+ fileInfo.children.foreach{ addRecursively(it) }
+ }
+
+ /* Deletes file from file system recursively, notifying listeners */
+ private fun deleteRecursively(file : VirtualFile) {
+ val fileInfoMaybe : VirtualFileInfo? = FileSystem.fileToInfo[file.path]
+ val fileInfo = fileInfoMaybe.sure()
+ fileInfo.children.foreach{ deleteRecursively(it) }
+ FileSystem.notifyEventHappened(VirtualFileDeletedEvent(file))
+ FileSystem.fileToInfo.remove(file)
+ }
+
+ /**
+ * Schedules refresh for given list of files.
+ */
+ public fun scheduleRefresh(files : List) {
+ taskQueue.put(files)
+ }
+
+ /**
+ * Schedules refresh for given list of files.
+ */
+ public fun scheduleRefresh(vararg files : VirtualFile) {
+ // FIXME This could be written more concise, using map() & toList() (KT-1164 & KT-1172)
+ val filesList = ArrayList()
+ files.foreach{ filesList.add(it) }
+ taskQueue.put(filesList)
+
+ // taskQueue.put(ArrayList(files.map{ it }))
+ // taskQueue.put(files.map{ it }.toList())
+ }
+
+ // FIXME RefreshQueue implements Runnable because of error on accessing
+ // private fields and methods from anonymous class (KT-1157 & KT-1159) (
+ override fun run() {
+ while (!workerThread.isInterrupted()) {
+ try {
+ takeAndRefreshFiles()
+ } catch (e : InterruptedException) {
+ }
+ }
+ }
+}
+
+// FIXME this method is used because of error on accessing
+// private methods from anonymous class (KT-1157) (
+private fun createTimerTask(task : () -> Unit) : TimerTask {
+ return object : TimerTask() {
+ override fun run() {
+ task()
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/example-vfs/src/Utils.kt b/examples/example-vfs/src/Utils.kt
new file mode 100644
index 00000000000..7926599c927
--- /dev/null
+++ b/examples/example-vfs/src/Utils.kt
@@ -0,0 +1,42 @@
+package org.jetbrains.jet.samples.vfs.utils;
+
+import java.util.concurrent.locks.Lock
+import java.util.List
+import std.util.*
+
+/**
+ * Executes task with given lock acquired.
+ */
+private fun locked(lock : Lock, body : () -> T) : T {
+ lock.lock()
+ try {
+ return body()
+ } finally {
+ lock.unlock();
+ }
+}
+
+/**
+ * Checks boolean value which should be true. If it is false, throws Assertion Error
+ *
+ * @param value value to be checked
+ */
+public fun assert(value : Boolean) {
+ if (!value) {
+ throw AssertionError()
+ }
+}
+
+/**
+ * Returns list containing all elements which first contains and second does not.
+ */
+public fun listDifference(first : List, second : List) : List {
+ return first.filter{ !second.contains(it) }.toList()
+}
+
+/**
+ * Returns list containing all elements which both lists contain.
+ */
+public fun listIntersection(first : List, second : List) : List {
+ return first.filter{ second.contains(it) }.toList()
+}
diff --git a/examples/example-vfs/src/VirtualFile.kt b/examples/example-vfs/src/VirtualFile.kt
new file mode 100644
index 00000000000..f848cedf4c7
--- /dev/null
+++ b/examples/example-vfs/src/VirtualFile.kt
@@ -0,0 +1,164 @@
+package org.jetbrains.jet.samples.vfs;
+
+import org.jetbrains.jet.samples.vfs.utils.*;
+import java.io.File
+import java.io.InputStream
+import java.io.FileInputStream
+import java.util.ArrayList
+import java.util.List
+
+import std.util.*
+
+/**
+ * Abstract virtual file.
+ */
+public abstract class VirtualFile(public val path : String) {
+ // FIXME this method should be replaced with val (KT-1168, KT-1170)
+ protected abstract fun kind() : String
+
+ // FIXME these abstract methods should be replaced with vals (KT-1165)
+ /**
+ * Returns file size.
+ */
+ public abstract fun size() : Long
+ /**
+ * Returns the time that the virtual file was last modified (milliseconds since
+ * the epoch (00:00:00 GMT, January 1, 1970).
+ */
+ public abstract fun modificationTime() : Long
+ /**
+ * Returns if virtual file exists.
+ */
+ public abstract fun exists() : Boolean
+ /**
+ * Returns if virtual file is directory
+ */
+ public abstract fun isDirectory() : Boolean
+ /**
+ * Returns list of virtual files which are children for this.
+ */
+ public abstract fun children() : List
+
+ /**
+ * Opens input stream for reading. After reading, stream should be closed.
+ * Reading from stream should be performed with read lock acquired.
+ */
+ public abstract fun openInputStream() : InputStream
+
+ fun equals(other : Any?) : Boolean {
+ return other is VirtualFile && kind() == other.kind() && path == other.path
+ }
+
+ fun hashCode() : Int {
+ // FIXME rewrite without casting when it will be possible
+ return (kind() as java.lang.String).hashCode() * 31 + (path as java.lang.String).hashCode()
+ }
+
+ fun toString(): String {
+ return "${kind()}[path=$path]"
+ }
+}
+
+/**
+ * Type of virtual file which corresponds to real file in file system of OS.
+ */
+public class PhysicalVirtualFile(path : String) : VirtualFile(path) {
+ override fun kind() : String = "Physical"
+
+ private val ioFile : File
+ get() = File(this.path.toSystemDependentPath())
+
+ override fun exists(): Boolean {
+ FileSystem.assertCanRead()
+ return ioFile.exists()
+ }
+
+ override fun size(): Long {
+ FileSystem.assertCanRead()
+ return ioFile.length()
+ }
+
+ override fun modificationTime(): Long {
+ FileSystem.assertCanRead()
+ return ioFile.lastModified()
+ }
+
+ override fun isDirectory(): Boolean {
+ FileSystem.assertCanRead()
+ return ioFile.isDirectory()
+ }
+
+ override fun children(): List {
+ FileSystem.assertCanRead()
+ return (ioFile.listFiles() ?: Array(0)).
+ map{ FileSystem.getFileByIoFile(it.sure()) }?.toList()
+ }
+
+ override fun openInputStream(): InputStream {
+ FileSystem.assertCanRead()
+ if (isDirectory()) {
+ throw IllegalArgumentException("Can't open directory for reading");
+ }
+ return CheckedInputStream(FileInputStream(ioFile))
+ }
+}
+
+private fun String.toSystemDependentPath() : String {
+ // FIXME constants should be extracted (NPE in compiler, KT-1111)
+ return this.replaceAll("/", java.io.File.separator.sure())
+}
+
+private fun String.toSystemIndependentPath() : String {
+ // FIXME constants should be extracted (NPE in compiler, KT-1111)
+ return this.replaceAll(java.io.File.separator.sure(), "/")
+}
+
+/**
+ * InputStream wrapper which checks that file system read lock is acquired on each operation.
+ */
+private class CheckedInputStream(private val wrapped : InputStream) : InputStream() {
+ override fun read(): Int {
+ FileSystem.assertCanRead()
+ return wrapped.read()
+ }
+
+ override fun read(b: ByteArray?, off: Int, len: Int) : Int {
+ FileSystem.assertCanRead()
+ return wrapped.read(b, off, len)
+ }
+
+ override fun markSupported(): Boolean {
+ FileSystem.assertCanRead()
+ return wrapped.markSupported()
+ }
+
+ override fun skip(n: Long): Long {
+ FileSystem.assertCanRead()
+ return wrapped.skip(n)
+ }
+
+ override fun close() {
+ FileSystem.assertCanRead()
+ return wrapped.close()
+ }
+
+ override fun mark(readlimit: Int) {
+ FileSystem.assertCanRead()
+ return wrapped.mark(readlimit)
+ }
+
+ override fun read(b: ByteArray?): Int {
+ FileSystem.assertCanRead()
+ return wrapped.read(b)
+ }
+
+ override fun reset() {
+ FileSystem.assertCanRead()
+ return wrapped.reset()
+ }
+
+ override fun available(): Int {
+ FileSystem.assertCanRead()
+ return wrapped.available()
+ }
+}
\ No newline at end of file
diff --git a/examples/example-vfs/src/VirtualFileEvent.kt b/examples/example-vfs/src/VirtualFileEvent.kt
new file mode 100644
index 00000000000..7f3d043a5c4
--- /dev/null
+++ b/examples/example-vfs/src/VirtualFileEvent.kt
@@ -0,0 +1,42 @@
+package org.jetbrains.jet.samples.vfs;
+
+/**
+ * Listener for receiving virtual file event
+ */
+public trait VirtualFileListener : java.util.EventListener {
+ fun eventHappened(val event : VirtualFileEvent)
+}
+
+/**
+ * Base type of virtual file events
+ * @property file affected by event
+ */
+public abstract class VirtualFileEvent(val file : VirtualFile) : Object() {
+}
+
+/**
+ * Event of creating file
+ */
+public class VirtualFileCreateEvent(file : VirtualFile) : VirtualFileEvent(file) {
+ override fun toString(): String? {
+ return "created ${file}"
+ }
+}
+
+/**
+ * Event of deleting file
+ */
+public class VirtualFileDeletedEvent(file : VirtualFile) : VirtualFileEvent(file) {
+ override fun toString(): String? {
+ return "deleted ${file}"
+ }
+}
+
+/**
+ * Event of changing file contents
+ */
+public class VirtualFileChangedEvent(file : VirtualFile) : VirtualFileEvent(file) {
+ override fun toString(): String? {
+ return "changed ${file}"
+ }
+}