PageRenderTime 51ms CodeModel.GetById 14ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 19package org.fusesource.scalate.util
 20
 21import java.io._
 22import java.util.zip.{ ZipEntry, ZipInputStream }
 23import java.net.URL
 24import scala.util.parsing.input.{ Position, OffsetPosition }
 25
 26import scala.language.implicitConversions
 27
 28object IOUtil {
 29
 30  val log = Log(getClass); import log._
 31
 32  class InvalidDirectiveException(directive: String, pos: Position) extends RuntimeException(directive + " at " + pos, null)
 33
 34  /**
 35   * Allows a File to be converted to a FileResource which also provides a Rich API for files
 36   */
 37  implicit def toResource(file: File) = FileResource(file, file.getPath)
 38  implicit def toFile(resource: FileResource): File = resource.asFile
 39
 40  /**
 41   * Creates any parent directories of the given path if they do not exist
 42   */
 43  def makeParentDirs(fileName: String): Unit = makeParentDirs(new File(fileName))
 44
 45  /**
 46   * Creates any parent directories of the given directory if they do not exist
 47   */
 48  def makeParentDirs(file: File): Unit = {
 49    val parent = file.getParentFile
 50    if (parent != null) {
 51      parent.mkdirs
 52    }
 53  }
 54
 55  /**
 56   * Recursively deletes a file and all of it's children files if it's a directory.
 57   */
 58  def recursiveDelete(file: File): Boolean = {
 59    if (file.isDirectory) {
 60      val children = file.listFiles
 61      if (children != null) {
 62        for (child <- children) {
 63          recursiveDelete(child)
 64        }
 65      }
 66    }
 67    file.delete
 68  }
 69
 70  val includeRegEx = """@@include\(\"(.+)\"\)""".r
 71
 72  /**
 73   * TODO: maybe we want other precompile directives at some point,
 74   * so this may need to be made more flexible
 75   */
 76  def mergeIncludes(sourceCode: String, encoding: String = "UTF-8"): String = {
 77    val matches = includeRegEx.findAllIn(sourceCode)
 78    if (!matches.hasNext) sourceCode
 79    else {
 80      matches.foldLeft(sourceCode) { (result, include) =>
 81        val includeSource: String = try {
 82          val includeRegEx(fileName) = include
 83          loadTextFile(new java.io.File(fileName), encoding)
 84        } catch {
 85          case m: MatchError =>
 86            throw new InvalidDirectiveException("include", OffsetPosition(include, 0))
 87          case n: FileNotFoundException => throw n
 88        }
 89        result.replace(include, includeSource)
 90      }
 91    }
 92  }
 93
 94  def loadText(in: InputStream, encoding: String = "UTF-8"): String = {
 95    val sourceCode = new String(loadBytes(in), encoding)
 96    mergeIncludes(sourceCode, encoding)
 97  }
 98
 99  def loadTextFile(path: File, encoding: String = "UTF-8") = {
100    val sourceCode = new String(loadBinaryFile(path), encoding)
101    mergeIncludes(sourceCode, encoding)
102  }
103
104  def loadBinaryFile(path: File): Array[Byte] = {
105    val baos = new ByteArrayOutputStream
106    val in = new FileInputStream(path)
107    try {
108      copy(in, baos)
109    } finally {
110      in.close
111    }
112
113    baos.toByteArray
114  }
115
116  def loadBytes(in: InputStream): Array[Byte] = {
117    val baos = new ByteArrayOutputStream
118    try {
119      copy(in, baos)
120    } finally {
121      in.close
122    }
123    baos.toByteArray
124  }
125
126  def writeText(path: String, text: String): Unit = writeText(new File(path), text)
127
128  def writeText(path: File, text: String): Unit = writeText(new FileWriter(path), text)
129
130  def writeText(stream: OutputStream, text: String): Unit = writeText(new OutputStreamWriter(stream), text)
131
132  def writeText(out: Writer, text: String): Unit = {
133    try {
134      out.write(text)
135    } finally {
136      out.close
137    }
138  }
139
140  def writeBinaryFile(path: String, contents: Array[Byte]): Unit = writeBinaryFile(new File(path), contents)
141
142  def writeBinaryFile(path: File, contents: Array[Byte]): Unit = {
143    val out = new FileOutputStream(path)
144    try {
145      out.write(contents)
146    } finally {
147      out.close
148    }
149  }
150
151  def copy(in: File, out: File): Long = {
152    out.getParentFile.mkdirs
153    copy(new FileInputStream(in), new FileOutputStream(out))
154  }
155
156  def copy(file: File, out: OutputStream): Long = copy(new BufferedInputStream(new FileInputStream(file)), out)
157
158  def copy(in: InputStream, file: File): Long = {
159    val out = new FileOutputStream(file)
160    try {
161      copy(in, out)
162    } finally {
163      out.close
164    }
165  }
166
167  def copy(url: URL, file: File): Long = {
168    val in = url.openStream
169    try {
170      copy(in, file)
171    } finally {
172      in.close
173    }
174  }
175
176  // For ARM
177  def using[R, C <: Closeable](c: C)(func: (C) => R): R = {
178    try {
179      func(c)
180    } finally {
181      try {
182        c.close
183      } catch {
184        case _: Exception => // ignore
185      }
186    }
187  }
188  def copy(in: InputStream, out: OutputStream): Long = {
189    var bytesCopied: Long = 0
190    val buffer = new Array[Byte](8192)
191
192    var bytes = in.read(buffer)
193    while (bytes >= 0) {
194      out.write(buffer, 0, bytes)
195      bytesCopied += bytes
196      bytes = in.read(buffer)
197    }
198
199    bytesCopied
200  }
201
202  def copy(in: Reader, out: Writer): Long = {
203    var charsCopied: Long = 0
204    val buffer = new Array[Char](8192)
205
206    var chars = in.read(buffer)
207    while (chars >= 0) {
208      out.write(buffer, 0, chars)
209      charsCopied += chars
210      chars = in.read(buffer)
211    }
212
213    charsCopied
214  }
215
216  /**
217   * Unjars the given stream for entries which match the optional filter to the given directory
218   */
219  def unjar(outputDir: File, input: InputStream, filter: ZipEntry => Boolean = allZipEntries): Unit = {
220    val zip = new ZipInputStream(input)
221    try {
222      val buffer = new Array[Byte](64 * 1024)
223      var ok = true
224      while (ok) {
225        val entry = zip.getNextEntry
226        if (entry == null) {
227          ok = false
228        } else {
229          val name = entry.getName
230          if (!entry.isDirectory && filter(entry)) {
231            debug("processing resource: %s", name)
232            val file = new File(outputDir.getCanonicalPath + "/" + name)
233            file.getParentFile.mkdirs
234            val bos = new FileOutputStream(file)
235            try {
236              var bytes = 1
237              while (bytes > 0) {
238                bytes = zip.read(buffer)
239                if (bytes > 0) {
240                  bos.write(buffer, 0, bytes)
241                }
242              }
243            } finally {
244              bos.close
245            }
246          }
247          zip.closeEntry
248        }
249      }
250    } finally {
251      zip.close
252    }
253  }
254
255  /**
256   * Recursively deletes the directory and all its children which match the optional filter
257   */
258  def recursiveDelete(file: File, filter: File => Boolean = allFiles): Unit = {
259    if (file.exists) {
260      if (file.isDirectory) {
261        for (c <- file.listFiles) {
262          recursiveDelete(c)
263        }
264      }
265      if (filter(file)) {
266        file.delete
267      }
268    }
269  }
270
271  protected def allZipEntries(entry: ZipEntry): Boolean = true
272
273  protected def allFiles(file: File): Boolean = true
274}