diff --git a/libraries/stdlib/src/JavaIo.kt b/libraries/stdlib/src/JavaIo.kt index e687c90a655..b5ab16c93bb 100644 --- a/libraries/stdlib/src/JavaIo.kt +++ b/libraries/stdlib/src/JavaIo.kt @@ -4,28 +4,70 @@ import java.io.* import java.nio.charset.* import java.util.NoSuchElementException -inline fun print(message : Any?) { System.out?.print(message) } -inline fun print(message : Int) { System.out?.print(message) } -inline fun print(message : Long) { System.out?.print(message) } -inline fun print(message : Byte) { System.out?.print(message) } -inline fun print(message : Short) { System.out?.print(message) } -inline fun print(message : Char) { System.out?.print(message) } -inline fun print(message : Boolean) { System.out?.print(message) } -inline fun print(message : Float) { System.out?.print(message) } -inline fun print(message : Double) { System.out?.print(message) } -inline fun print(message : CharArray) { System.out?.print(message) } +inline fun print(message : Any?) { + System.out?.print(message) +} +inline fun print(message : Int) { + System.out?.print(message) +} +inline fun print(message : Long) { + System.out?.print(message) +} +inline fun print(message : Byte) { + System.out?.print(message) +} +inline fun print(message : Short) { + System.out?.print(message) +} +inline fun print(message : Char) { + System.out?.print(message) +} +inline fun print(message : Boolean) { + System.out?.print(message) +} +inline fun print(message : Float) { + System.out?.print(message) +} +inline fun print(message : Double) { + System.out?.print(message) +} +inline fun print(message : CharArray) { + System.out?.print(message) +} -inline fun println(message : Any?) { System.out?.println(message) } -inline fun println(message : Int) { System.out?.println(message) } -inline fun println(message : Long) { System.out?.println(message) } -inline fun println(message : Byte) { System.out?.println(message) } -inline fun println(message : Short) { System.out?.println(message) } -inline fun println(message : Char) { System.out?.println(message) } -inline fun println(message : Boolean) { System.out?.println(message) } -inline fun println(message : Float) { System.out?.println(message) } -inline fun println(message : Double) { System.out?.println(message) } -inline fun println(message : CharArray) { System.out?.println(message) } -inline fun println() { System.out?.println() } +inline fun println(message : Any?) { + System.out?.println(message) +} +inline fun println(message : Int) { + System.out?.println(message) +} +inline fun println(message : Long) { + System.out?.println(message) +} +inline fun println(message : Byte) { + System.out?.println(message) +} +inline fun println(message : Short) { + System.out?.println(message) +} +inline fun println(message : Char) { + System.out?.println(message) +} +inline fun println(message : Boolean) { + System.out?.println(message) +} +inline fun println(message : Float) { + System.out?.println(message) +} +inline fun println(message : Double) { + System.out?.println(message) +} +inline fun println(message : CharArray) { + System.out?.println(message) +} +inline fun println() { + System.out?.println() +} private val stdin : BufferedReader = BufferedReader(InputStreamReader(object : InputStream() { override fun read() : Int { @@ -69,54 +111,54 @@ inline fun readLine() : String? = stdin.readLine() /** Uses the given resource then closes it down correctly whether an exception is thrown or not */ inline fun T.use(block: (T)-> R) : R { - var closed = false - try { - return block(this) - } catch (e: Exception) { - closed = true + var closed = false try { - this.close() - } catch (closeException: Exception) { - // eat the closeException as we are already throwing the original cause - // and we don't want to mask the real exception + return block(this) + } catch (e: Exception) { + closed = true + try { + this.close() + } catch (closeException: Exception) { + // eat the closeException as we are already throwing the original cause + // and we don't want to mask the real exception - // TODO on Java 7 we should call - // e.addSuppressed(closeException) - // to work like try-with-resources - // http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html#suppressed-exceptions + // TODO on Java 7 we should call + // e.addSuppressed(closeException) + // to work like try-with-resources + // http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html#suppressed-exceptions + } + throw e + } finally { + if (!closed) { + this.close() + } } - throw e - } finally { - if (!closed) { - this.close() - } - } } fun InputStream.iterator() : ByteIterator = - object: ByteIterator() { - override val hasNext : Boolean - get() = available() > 0 +object: ByteIterator() { + override val hasNext : Boolean + get() = available() > 0 - override fun nextByte() = read().toByte() - } + override fun nextByte() = read().toByte() +} inline fun InputStream.buffered(bufferSize: Int) = BufferedInputStream(this, bufferSize) inline val InputStream.reader : InputStreamReader - get() = InputStreamReader(this) +get() = InputStreamReader(this) inline val InputStream.bufferedReader : BufferedReader - get() = BufferedReader(reader) +get() = BufferedReader(reader) -inline fun InputStream.reader(charset: Charset) : InputStreamReader = InputStreamReader(this, charset) +inline fun InputStream.reader(charset: Charset) : InputStreamReader = InputStreamReader(this, charset) inline fun InputStream.reader(charsetName: String) = InputStreamReader(this, charsetName) inline fun InputStream.reader(charsetDecoder: CharsetDecoder) = InputStreamReader(this, charsetDecoder) inline val InputStream.buffered : BufferedInputStream - get() = if(this is BufferedInputStream) this else BufferedInputStream(this) +get() = if(this is BufferedInputStream) this else BufferedInputStream(this) // inline val Reader.buffered : BufferedReader // get() = if(this is BufferedReader) this else BufferedReader(this) @@ -126,13 +168,13 @@ inline fun Reader.buffered(): BufferedReader = if(this is BufferedReader) this e inline fun Reader.buffered(bufferSize: Int) = BufferedReader(this, bufferSize) inline fun Reader.foreachLine(block: (String) -> Unit): Unit { - this.use{ - val iter = buffered().lineIterator() - while (iter.hasNext) { - val elem = iter.next() - block(elem) + this.use{ + val iter = buffered().lineIterator() + while (iter.hasNext) { + val elem = iter.next() + block(elem) + } } - } } inline fun Reader.useLines(block: (Iterator) -> T): T = this.buffered().use{block(it.lineIterator())} @@ -147,25 +189,131 @@ inline fun Reader.useLines(block: (Iterator) -> T): T = this.buffere inline fun BufferedReader.lineIterator() : Iterator = LineIterator(this) protected class LineIterator(val reader: BufferedReader) : Iterator { - private var nextValue: String? = null - private var done = false + private var nextValue: String? = null + private var done = false - override val hasNext: Boolean + override val hasNext: Boolean get() { - if (nextValue == null && !done) { - nextValue = reader.readLine() - if (nextValue == null) done = true - } - return nextValue != null + if (nextValue == null && !done) { + nextValue = reader.readLine() + if (nextValue == null) done = true + } + return nextValue != null } - override fun next(): String { - if (!hasNext) { - throw NoSuchElementException() + override fun next(): String { + if (!hasNext) { + throw NoSuchElementException() + } + val answer = nextValue + nextValue = null + return answer.sure() } - val answer = nextValue - nextValue = null - return answer.sure() - } } + + +/** + * Recursively process this file and all children with the given block + */ +fun File.recurse(block: (File) -> Unit): Unit { + block(this) + if (this.isDirectory()) { + for (child in this.listFiles()) { + if (child != null) { + child.recurse(block) + } + } + } +} + +/** + * Returns this if the file is a directory or the parent if its a file inside a directory + */ +inline val File.directory: File +get() = if (this.isDirectory()) this else this.getParentFile().sure() + +/** + * Returns the canoncial path of the file + */ +inline val File.canonicalPath: String +get() = getCanonicalPath() ?: "" + +/** + * Returns the file name or "" for an empty name + */ +inline val File.name: String +get() = getName() ?: "" + +/** + * Returns the file path or "" for an empty name + */ +inline val File.path: String +get() = getPath() ?: "" + +/** + * Returns true if the file ends with the given extension + */ +inline val File.extension: String +get() { + val text = this.name + val idx = text.lastIndexOf('.') + return if (idx >= 0) { + text.substring(idx + 1) + } else { + "" + } +} + +/** +* Returns true if the given file is in the same directory or a descendant directory +*/ +fun File.isDescendant(file: File): Boolean { + return file.directory.canonicalPath.startsWith(this.directory.canonicalPath) +} + +/** +* Loads the text file as a String +*/ +fun File.loadText(): String { + val buffer = StringWriter() + FileReader(this).use { + it.copyTo(buffer) + } + return buffer.toString() ?: "" +} + +/** +* Copies this stream to the given output stream, returning the number of bytes copied +* +* **Note** it is the callers responsibility to close both of these resources +*/ +fun InputStream.copyTo(out: OutputStream, bufferSize: Int = 64 * 1024): Long { + var bytesCopied: Long = 0 + val buffer = ByteArray(bufferSize) + var bytes = read(buffer) + while (bytes >= 0) { + out.write(buffer, 0, bytes) + bytesCopied += bytes + bytes = read(buffer) + } + return bytesCopied +} + + +/** +* Copies this reader to the given output writer, returning the number of bytes copied. +* +* **Note** it is the callers responsibility to close both of these resources +*/ +fun Reader.copyTo(out: Writer, bufferSize: Int = 64 * 1024): Long { + var charsCopied: Long = 0 + val buffer = CharArray(bufferSize) + var chars = read(buffer) + while (chars >= 0) { + out.write(buffer, 0, chars) + charsCopied += chars + chars = read(buffer) + } + return charsCopied +}