Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2012 Taro L. Saito
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//--------------------------------------
//
// Resource.scala
// Since: 2012/07/17 9:12
//
//--------------------------------------
package xerial.core.io
import java.io.{InputStreamReader, BufferedReader, File, BufferedInputStream}
import java.util.jar.JarFile
import java.lang.reflect.Modifier
import java.net.{URLClassLoader, URL}
import xerial.core.log.Logger
/**
* Extend this trait to add support your class for reading resources
*/
trait Resource {
def open[U](resourceFileName: String)(f: BufferedInputStream => U): U = {
Resource.open(this.asInstanceOf[AnyRef].getClass, resourceFileName)(f)
}
def openText[U](resourceFileName:String)(f:BufferedReader => U) : U = {
Resource.open(this.asInstanceOf[AnyRef].getClass, resourceFileName) { in =>
val r = new BufferedReader(new InputStreamReader(in))
try {
f(r)
}
finally {
r.close
}
}
}
}
/**
* Resource file manager.
*
* @author leo
*/
object Resource extends Logger {
/**
* Open a resource as a stream, then execute the code block using the stream
* @param referenceClass context class to specify the package containing the resource file
* @param resourceFileName file name
* @param body code block
* @tparam U
*/
def open[U](referenceClass: Class[_], resourceFileName: String)(body: BufferedInputStream => U): U = {
val u = find(referenceClass, resourceFileName)
if (u.isEmpty)
sys.error("Resource %s (in %s) not found".format(resourceFileName, referenceClass.getSimpleName))
val s = new BufferedInputStream(u.get.openStream())
try
body(s)
finally
s.close
}
private def packagePath(referenceClass: Class[_]): String = {
return packagePath(referenceClass.getPackage)
}
private def packagePath(basePackage: Package): String = {
return packagePath(basePackage.getName)
}
private def packagePath(packageName: String): String = {
val packageAsPath: String = packageName.replaceAll("\\.", "/")
if (packageAsPath.endsWith("/")) packageAsPath else packageAsPath + "/"
}
/**
* @return Stream of class loaders in the path from the specified class loader to the root class loader
*/
private def classLoaders(cl: ClassLoader): Stream[URLClassLoader] = {
def stream(c: ClassLoader): Stream[URLClassLoader] = {
c match {
case null => Stream.empty
case u: URLClassLoader => u #:: stream(c.getParent)
case _ => stream(c.getParent)
}
}
stream(cl)
}
/**
* @return Stream of class loaders in the path from current class loader to the root class loader
*/
private def classLoaders: Stream[ClassLoader] = classLoaders(Thread.currentThread.getContextClassLoader)
private def resolveResourcePath(packageName: String, resourceFileName: String) = {
val path: String = packagePath(packageName)
prependSlash(path + resourceFileName)
}
private def prependSlash(name: String): String = {
if (name.startsWith("/"))
name
else
"/" + name
}
def find(referenceClass: Class[_], resourceFileName: String): Option[URL] = {
find(packagePath(referenceClass), resourceFileName)
}
/**
* Find a resource from the give absolute path
* @param absoluteResourcePath
* @return
*/
def find(absoluteResourcePath: String): Option[URL] =
find("", if (absoluteResourcePath.startsWith("/")) absoluteResourcePath.substring(1) else absoluteResourcePath)
/**
* Finds the java.net.URL of the resource
*
* @param packageName
* the base package name to find the resource
* @param resourceFileName
* the resource file name relative to the package folder
* @return the URL of the specified resource
*/
def find(packageName: String, resourceFileName: String): Option[URL] = {
val resourcePath = resolveResourcePath(packageName, resourceFileName)
trace(s"search resource: $resourcePath")
val r = classLoaders.map(_.getResource(resourcePath)).
collectFirst {
case path: URL => path
}
r orElse Option(this.getClass.getResource(resourcePath))
}
/**
* VirtualFile is a common interface to handle system files and file resources in JAR.
*
* System file resources have an URL prefixed with "file:".
* e.g., "file:/C:/Program Files/Software/classes/org/xerial/util/FileResource.java"
* JAR file contents have an URL prefixed with "jar:file:
* e.g., "jar:file:/C:/Program Files/Software/something.jar!/org/xerial/util/FileResource.java"
*
* @author leo
*
*/
abstract trait VirtualFile {
/**
* Gets the logical path of the file.
* For example, if this VirtualFile' URL is "file:/somewhere/org/xerial/util/FileResource.java",
* its logical name is org/xerial/util/FileResource.java, beginning from the root package.
* @return
*/
def logicalPath: String
/**
* is directory?
* @return true when the file is a directory, otherwise false
*/
def isDirectory: Boolean
/**
* Gets the URL of this file
* @return
*/
def url: URL
}
/**
* A virtual file implementation for usual files
*
* @author leo
*
*/
case class SystemFile(file: java.io.File, logicalPath: String) extends VirtualFile {
def url: URL = file.toURI.toURL
def isDirectory: Boolean = file.isDirectory
}
/**
* A virtual file implementation for file resources contained in a JAR file
*
* @author leo
*
*/
case class FileInJar(resourceURL: URL, logicalPath: String, isDirectory: Boolean) extends VirtualFile {
if (resourceURL == null)
sys.error("resource URL cannot be null: " + logicalPath)
def url = resourceURL
}
private def extractLogicalName(packagePath: String, resourcePath: String): String = {
val p = if (!packagePath.endsWith("/")) packagePath + "/" else packagePath
val pos: Int = resourcePath.indexOf(p)
if (pos < 0) return null
val logicalName: String = resourcePath.substring(pos + p.length)
return logicalName
}
private def collectFileResources(resourceURLString: String, packagePath: String, resourceFilter: String => Boolean): Seq[VirtualFile] = {
val logicalName = extractLogicalName(packagePath, resourceURLString)
if (logicalName == null)
throw new IllegalArgumentException("packagePath=" + packagePath + ", resourceURL=" + resourceURLString)
trace(s"collect: logical name: $logicalName")
val b = Seq.newBuilder[VirtualFile]
val file: File = new File(new URL(resourceURLString).toURI)
if (resourceFilter(file.getPath))
b += SystemFile(file, logicalName)
if (file.isDirectory) {
for (childFile <- file.listFiles) {
val childResourceURL = resourceURLString + (if (resourceURLString.endsWith("/")) "" else "/") + childFile.getName
b ++= collectFileResources(childResourceURL, packagePath, resourceFilter)
}
}
b.result()
}
/**
* Create a list of all resources under the given resourceURL recursively. If the
* resourceURL is a file, this method searches directories under the path. If the resource is contained
* in a Jar file, it searches contents of the Jar file.
*
* @param resourceURL
* @param packageName package name under consideration
* @param resourceFilter
* @return the list of resources matching the given resource filter
*/
private def listResources(resourceURL: URL, packageName: String, resourceFilter: String => Boolean): Seq[VirtualFile] = {
trace(s"listResource: url=$resourceURL")
val pkgPath = packagePath(packageName)
val fileList = Seq.newBuilder[VirtualFile]
if (resourceURL == null)
return Seq.empty
val protocol = resourceURL.getProtocol
if (protocol == "file") {
val resourceURLString = resourceURL.toString
fileList ++= collectFileResources(resourceURLString, pkgPath, resourceFilter)
}
else if (protocol == "jar") {
val path: String = resourceURL.getPath
val pos: Int = path.indexOf("!")
if (pos < 0)
throw new IllegalArgumentException("invalid resource URL: " + resourceURL)
val jarPath = path.substring(0, pos) replaceAll("%20", " ")
val filePath = path.substring(0, pos) replaceAll("%20", " ") replace("file:", "")
val jarURLString = "jar:" + jarPath
val jf: JarFile = new JarFile(filePath)
val entryEnum = jf.entries
while (entryEnum.hasMoreElements) {
val jarEntry = entryEnum.nextElement
val physicalURL = jarURLString + "!/" + jarEntry.getName
trace(s"phisical URL: $physicalURL")
val jarFileURL = new URL(physicalURL)
val logicalName = extractLogicalName(pkgPath, jarEntry.getName)
trace(s"logical name of ${jarEntry.getName}: $logicalName (path:$pkgPath)")
if (logicalName != null && resourceFilter(logicalName))
fileList += FileInJar(jarFileURL, logicalName, jarEntry.isDirectory)
}
}
else {
throw new UnsupportedOperationException("resources other than file or jar are not supported: " + resourceURL)
}
fileList.result
}
/**
* Collect resources under the given package
* @param packageName
* @return
*/
def listResources(packageName: String): Seq[VirtualFile] =
listResources(packageName, {
f: String => true
})
/**
* Collect resources under the given package
* @param classLoader
* @param packageName
* @param resourceFilter
* @return
*/
def listResources(packageName: String, resourceFilter: String => Boolean, classLoader: ClassLoader = Thread.currentThread.getContextClassLoader): Seq[VirtualFile] = {
val b = Seq.newBuilder[VirtualFile]
for (u <- findResourceURLs(classLoader, packageName)) {
b ++= listResources(u, packageName, resourceFilter)
}
b.result
}
/**
* Find resource URLs that can be found from a given class loader and its ancestors
* @param cl class loader
* @param name resource name
* @return
*/
def findResourceURLs(cl: ClassLoader, name: String): Seq[URL] = {
val path = packagePath(name)
trace(s"find resource URLs: $path")
val b = Seq.newBuilder[URL]
for (c: URLClassLoader <- classLoaders(cl)) {
val e = c.findResources(path)
while (e.hasMoreElements)
b += e.nextElement
}
b.result
}
def findClasses[A](packageName: String, toSearch: Class[A], classLoader: ClassLoader = Thread.currentThread.getContextClassLoader): Seq[Class[A]] = {
val classFileList = listResources(packageName, {
f: String => f.endsWith(".class")
}, classLoader)
def componentName(path: String): Option[String] = {
val dot: Int = path.lastIndexOf(".")
if (dot <= 0)
None
else
Some(path.substring(0, dot).replaceAll("/", "."))
}
def findClass(name: String): Option[Class[_]] = {
try
Some(Class.forName(name, false, classLoader))
catch {
case e: ClassNotFoundException => None
}
}
val b = Seq.newBuilder[Class[A]]
for (vf <- classFileList; cn <- componentName(vf.logicalPath)) {
val className: String = packageName + "." + cn
for (cl <- findClass(className)) {
if (!Modifier.isAbstract(cl.getModifiers) && toSearch.isAssignableFrom(cl)) {
b += cl.asInstanceOf[Class[A]]
}
}
}
b.result
}
def findClasses[A](searchPath: Package, toSearch: Class[A], classLoader: ClassLoader): Seq[Class[A]] = {
findClasses(searchPath.getName, toSearch, classLoader)
}
}