PageRenderTime 81ms CodeModel.GetById 70ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://github.com/scalate/scalate
Scala | 275 lines | 111 code | 55 blank | 109 comment | 8 complexity | 64d78d08a1a6b1decd2d15c4af5cae30 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 */
 18package org.fusesource.scalate.util
 19
 20import io.Source
 21import java.io._
 22import java.net.{ URISyntaxException, URL }
 23
 24/**
 25 * Represents a string, file or URI based resource
 26 */
 27trait Resource {
 28
 29  /**
 30   * Returns the URI of the resource
 31   */
 32  def uri: String
 33
 34  /**
 35   * Returns the text content of the resource
 36   */
 37  def text: String = IOUtil.loadText(inputStream)
 38
 39  /**
 40   * Returns the reader of the content of the resource
 41   */
 42  def reader: Reader = new InputStreamReader(inputStream)
 43
 44  /**
 45   * Returns the input stream of the content of the resource
 46   */
 47  def inputStream: InputStream
 48
 49  /**
 50   * Returns the last modified time of the resource
 51   */
 52  def lastModified: Long
 53
 54  def toFile: Option[File] = None
 55}
 56
 57/**
 58 * Not all resources are writeable so this optional trait is for those
 59 */
 60trait WriteableResource extends Resource {
 61  /**
 62   * Writes text to the resource replacing its previous content
 63   */
 64  def text_=(value: String): Unit = IOUtil.writeText(outputStream, value)
 65
 66  /**
 67   * Returns the output stream of the resource
 68   */
 69  def outputStream: OutputStream
 70
 71  /**
 72   * Returns the writer to the content of the resource
 73   */
 74  def writer: Writer = new OutputStreamWriter(outputStream)
 75
 76}
 77
 78abstract class TextResource extends Resource {
 79  override def reader = new StringReader(text)
 80
 81  def inputStream = new ByteArrayInputStream(text.getBytes)
 82
 83  // just return current time as we have no way to know
 84  def lastModified: Long = System.currentTimeMillis
 85}
 86
 87case class StringResource(uri: String, override val text: String) extends TextResource
 88
 89case class UriResource(override val uri: String, resourceLoader: ResourceLoader) extends DelegateResource {
 90  protected def delegate = resourceLoader.resourceOrFail(uri)
 91}
 92
 93/**
 94 * Can act as a RichFile type interface too, adding a number of extra helper methods to make Files more rich
 95 */
 96case class FileResource(file: File, uri: String) extends WriteableResource {
 97  override def text = IOUtil.loadTextFile(file)
 98
 99  override def reader = new FileReader(file)
100
101  def inputStream = new FileInputStream(file)
102
103  def outputStream = new FileOutputStream(file)
104
105  def lastModified = file.lastModified
106
107  /**
108   * Create a child file
109   */
110  def /(name: String) = FileResource(new File(file, name), uri + "/" + name)
111
112  implicit def asFile: File = file
113
114  override def toFile = Some(file)
115
116  def name = file.getName
117
118  /**
119   * Returns the extension of the file
120   */
121  def extension = Files.extension(name)
122
123  /**
124   * Returns the name of the file without its extension
125   */
126  def nameDropExtension = Files.dropExtension(name)
127
128  /**
129   * Recursively finds the first file in this directory that matches the given
130   * predicate or matches against this file for non-directories
131   */
132  def recursiveFind(f: File => Boolean): Option[File] = Files.recursiveFind(file)(f)
133
134  /**
135   * Returns an Iterable over the immediate children of this directory
136   * or an empty Iterable
137   */
138  def children: Iterable[File] = Files.children(file)
139
140  /**
141   * Returns an Iterable over any descending files
142   */
143  def descendants: Iterable[File] = Files.descendants(file)
144
145  /**
146   * Returns an Iterable over this file and any descending files
147   */
148  def andDescendants: Iterable[File] = Files.andDescendants(file)
149
150  /**
151   * Returns the relative URI of this file from the given root directory
152   */
153  def relativeUri(root: File) = Files.relativeUri(root, file)
154}
155
156object URLResource extends Log
157case class URLResource(url: URL) extends WriteableResource {
158  import URLResource._
159  def uri = url.toExternalForm
160
161  lazy val connection = url.openConnection
162
163  def inputStream = url.openStream
164
165  def outputStream = connection.getOutputStream
166
167  def lastModified = {
168    val con = url.openConnection
169    con.getLastModified
170  }
171
172  override def toFile: Option[File] = {
173    var f: File = null
174    if (url.getProtocol == "file") {
175      try {
176        try {
177          f = new File(url.toURI)
178        } catch {
179          case e: URISyntaxException => f = new File(url.getPath)
180        }
181      } catch {
182        case e: ThreadDeath => throw e
183        case e: VirtualMachineError => throw e
184        case e: Exception => debug(e, "While converting " + url + " to a File I caught: " + e)
185      }
186    }
187    if (f != null && f.exists && f.isFile) {
188      Some(f)
189    } else {
190      None
191    }
192  }
193}
194
195case class SourceResource(uri: String, source: Source) extends TextResource {
196  override def text = {
197    val builder = new StringBuilder
198    val s: Source = source.pos match {
199      case 0 => source
200      case _ => {
201        source.close()
202        source.reset()
203      }
204    }
205    for (c <- s) {
206      builder.append(c)
207    }
208    builder.toString
209  }
210}
211
212abstract class DelegateResource extends Resource {
213  override def uri = delegate.uri
214
215  override def text = delegate.text
216
217  override def reader = delegate.reader
218
219  override def inputStream = delegate.inputStream
220
221  def lastModified = delegate.lastModified
222
223  protected def delegate: Resource
224}
225
226/**
227 * Helper methods to create a  [[org.fusesource.scalate.support.Resource]] from various sources
228 */
229object Resource {
230
231  /**
232   * Creates a [[org.fusesource.scalate.support.Resource]] from the actual String contents using the given
233   * URI.
234   *
235   * The URI is used to determine the package name to put the template in along with
236   * the template kind (using the extension of the URI)
237   */
238  def fromText(uri: String, templateText: String) = StringResource(uri, templateText)
239
240  /**
241   * Creates a [[org.fusesource.scalate.support.Resource]] from a local URI such as in a web application using the
242   * class loader to resolve URIs to actual resources
243   */
244  def fromUri(uri: String, resourceLoader: ResourceLoader) = UriResource(uri, resourceLoader)
245
246  /**
247   * Creates a [[org.fusesource.scalate.support.Resource]] from a file
248   */
249  def fromFile(file: File): FileResource = fromFile(file, file.getPath)
250
251  def fromFile(file: File, uri: String): FileResource = FileResource(file, uri)
252
253  /**
254   * Creates a [[org.fusesource.scalate.support.Resource]] from a file name
255   */
256  def fromFile(fileName: String): FileResource = fromFile(new File(fileName))
257
258  /**
259   * Creates a [[org.fusesource.scalate.support.Resource]] from a URL
260   */
261  def fromURL(url: URL): URLResource = URLResource(url)
262
263  /**
264   * Creates a [[org.fusesource.scalate.support.Resource]] from a URL
265   */
266  def fromURL(url: String): URLResource = fromURL(new URL(url))
267
268  /**
269   * Creates a [[org.fusesource.scalate.support.Resource]] from the [[scala.io.Source]] and the given URI.
270   *
271   * The URI is used to determine the package name to put the template in along with
272   * the template kind (using the extension of the URI)
273   */
274  def fromSource(uri: String, source: Source) = SourceResource(uri, source)
275}