/scalate-util/src/main/scala/org/fusesource/scalate/util/IOUtil.scala

http://github.com/scalate/scalate · Scala · 274 lines · 197 code · 37 blank · 40 comment · 22 complexity · 0cd1f1aae548c5105189ab8aebb7b35e MD5 · raw file

  1. /**
  2. * Copyright (C) 2009-2011 the original author or authors.
  3. * See the notice.md file distributed with this work for additional
  4. * information regarding copyright ownership.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package org.fusesource.scalate.util
  19. import java.io._
  20. import java.util.zip.{ ZipEntry, ZipInputStream }
  21. import java.net.URL
  22. import scala.util.parsing.input.{ Position, OffsetPosition }
  23. import scala.language.implicitConversions
  24. object IOUtil {
  25. val log = Log(getClass); import log._
  26. class InvalidDirectiveException(directive: String, pos: Position) extends RuntimeException(directive + " at " + pos, null)
  27. /**
  28. * Allows a File to be converted to a FileResource which also provides a Rich API for files
  29. */
  30. implicit def toResource(file: File) = FileResource(file, file.getPath)
  31. implicit def toFile(resource: FileResource): File = resource.asFile
  32. /**
  33. * Creates any parent directories of the given path if they do not exist
  34. */
  35. def makeParentDirs(fileName: String): Unit = makeParentDirs(new File(fileName))
  36. /**
  37. * Creates any parent directories of the given directory if they do not exist
  38. */
  39. def makeParentDirs(file: File): Unit = {
  40. val parent = file.getParentFile
  41. if (parent != null) {
  42. parent.mkdirs
  43. }
  44. }
  45. /**
  46. * Recursively deletes a file and all of it's children files if it's a directory.
  47. */
  48. def recursiveDelete(file: File): Boolean = {
  49. if (file.isDirectory) {
  50. val children = file.listFiles
  51. if (children != null) {
  52. for (child <- children) {
  53. recursiveDelete(child)
  54. }
  55. }
  56. }
  57. file.delete
  58. }
  59. val includeRegEx = """@@include\(\"(.+)\"\)""".r
  60. /**
  61. * TODO: maybe we want other precompile directives at some point,
  62. * so this may need to be made more flexible
  63. */
  64. def mergeIncludes(sourceCode: String, encoding: String = "UTF-8"): String = {
  65. val matches = includeRegEx.findAllIn(sourceCode)
  66. if (!matches.hasNext) sourceCode
  67. else {
  68. matches.foldLeft(sourceCode) { (result, include) =>
  69. val includeSource: String = try {
  70. val includeRegEx(fileName) = include
  71. loadTextFile(new java.io.File(fileName), encoding)
  72. } catch {
  73. case m: MatchError =>
  74. throw new InvalidDirectiveException("include", OffsetPosition(include, 0))
  75. case n: FileNotFoundException => throw n
  76. }
  77. result.replace(include, includeSource)
  78. }
  79. }
  80. }
  81. def loadText(in: InputStream, encoding: String = "UTF-8"): String = {
  82. val sourceCode = new String(loadBytes(in), encoding)
  83. mergeIncludes(sourceCode, encoding)
  84. }
  85. def loadTextFile(path: File, encoding: String = "UTF-8") = {
  86. val sourceCode = new String(loadBinaryFile(path), encoding)
  87. mergeIncludes(sourceCode, encoding)
  88. }
  89. def loadBinaryFile(path: File): Array[Byte] = {
  90. val baos = new ByteArrayOutputStream
  91. val in = new FileInputStream(path)
  92. try {
  93. copy(in, baos)
  94. } finally {
  95. in.close
  96. }
  97. baos.toByteArray
  98. }
  99. def loadBytes(in: InputStream): Array[Byte] = {
  100. val baos = new ByteArrayOutputStream
  101. try {
  102. copy(in, baos)
  103. } finally {
  104. in.close
  105. }
  106. baos.toByteArray
  107. }
  108. def writeText(path: String, text: String): Unit = writeText(new File(path), text)
  109. def writeText(path: File, text: String): Unit = writeText(new FileWriter(path), text)
  110. def writeText(stream: OutputStream, text: String): Unit = writeText(new OutputStreamWriter(stream), text)
  111. def writeText(out: Writer, text: String): Unit = {
  112. try {
  113. out.write(text)
  114. } finally {
  115. out.close
  116. }
  117. }
  118. def writeBinaryFile(path: String, contents: Array[Byte]): Unit = writeBinaryFile(new File(path), contents)
  119. def writeBinaryFile(path: File, contents: Array[Byte]): Unit = {
  120. val out = new FileOutputStream(path)
  121. try {
  122. out.write(contents)
  123. } finally {
  124. out.close
  125. }
  126. }
  127. def copy(in: File, out: File): Long = {
  128. out.getParentFile.mkdirs
  129. copy(new FileInputStream(in), new FileOutputStream(out))
  130. }
  131. def copy(file: File, out: OutputStream): Long = copy(new BufferedInputStream(new FileInputStream(file)), out)
  132. def copy(in: InputStream, file: File): Long = {
  133. val out = new FileOutputStream(file)
  134. try {
  135. copy(in, out)
  136. } finally {
  137. out.close
  138. }
  139. }
  140. def copy(url: URL, file: File): Long = {
  141. val in = url.openStream
  142. try {
  143. copy(in, file)
  144. } finally {
  145. in.close
  146. }
  147. }
  148. // For ARM
  149. def using[R, C <: Closeable](c: C)(func: (C) => R): R = {
  150. try {
  151. func(c)
  152. } finally {
  153. try {
  154. c.close
  155. } catch {
  156. case _: Exception => // ignore
  157. }
  158. }
  159. }
  160. def copy(in: InputStream, out: OutputStream): Long = {
  161. var bytesCopied: Long = 0
  162. val buffer = new Array[Byte](8192)
  163. var bytes = in.read(buffer)
  164. while (bytes >= 0) {
  165. out.write(buffer, 0, bytes)
  166. bytesCopied += bytes
  167. bytes = in.read(buffer)
  168. }
  169. bytesCopied
  170. }
  171. def copy(in: Reader, out: Writer): Long = {
  172. var charsCopied: Long = 0
  173. val buffer = new Array[Char](8192)
  174. var chars = in.read(buffer)
  175. while (chars >= 0) {
  176. out.write(buffer, 0, chars)
  177. charsCopied += chars
  178. chars = in.read(buffer)
  179. }
  180. charsCopied
  181. }
  182. /**
  183. * Unjars the given stream for entries which match the optional filter to the given directory
  184. */
  185. def unjar(outputDir: File, input: InputStream, filter: ZipEntry => Boolean = allZipEntries): Unit = {
  186. val zip = new ZipInputStream(input)
  187. try {
  188. val buffer = new Array[Byte](64 * 1024)
  189. var ok = true
  190. while (ok) {
  191. val entry = zip.getNextEntry
  192. if (entry == null) {
  193. ok = false
  194. } else {
  195. val name = entry.getName
  196. if (!entry.isDirectory && filter(entry)) {
  197. debug("processing resource: %s", name)
  198. val file = new File(outputDir.getCanonicalPath + "/" + name)
  199. file.getParentFile.mkdirs
  200. val bos = new FileOutputStream(file)
  201. try {
  202. var bytes = 1
  203. while (bytes > 0) {
  204. bytes = zip.read(buffer)
  205. if (bytes > 0) {
  206. bos.write(buffer, 0, bytes)
  207. }
  208. }
  209. } finally {
  210. bos.close
  211. }
  212. }
  213. zip.closeEntry
  214. }
  215. }
  216. } finally {
  217. zip.close
  218. }
  219. }
  220. /**
  221. * Recursively deletes the directory and all its children which match the optional filter
  222. */
  223. def recursiveDelete(file: File, filter: File => Boolean = allFiles): Unit = {
  224. if (file.exists) {
  225. if (file.isDirectory) {
  226. for (c <- file.listFiles) {
  227. recursiveDelete(c)
  228. }
  229. }
  230. if (filter(file)) {
  231. file.delete
  232. }
  233. }
  234. }
  235. protected def allZipEntries(entry: ZipEntry): Boolean = true
  236. protected def allFiles(file: File): Boolean = true
  237. }