
org.grails.io.support.GrailsResourceUtils Maven / Gradle / Ivy
/*
* Copyright 2002-2013 the original author or authors.
*
* 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.
*/
package org.grails.io.support;
import grails.util.BuildSettings;
import groovy.lang.Closure;
import groovy.util.ConfigObject;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility methods for resource handling / figuring out class names.
*
* @author Graeme Rocher
* @author Juergen Hoeller
* @since 2.0
*/
public class GrailsResourceUtils {
public static final String CLASS_EXTENSION = ".class";
private static final String WINDOWS_FOLDER_SEPARATOR = "\\";
private static final String TOP_PATH = "..";
private static final String CURRENT_PATH = ".";
private static final String FOLDER_SEPARATOR = "/";
public static final String JAR_URL_SEPARATOR = "!/";
/** Pseudo URL prefix for loading from the class path: "classpath:" */
public static final String CLASSPATH_URL_PREFIX = "classpath:";
/** URL prefix for loading from the file system: "file:" */
public static final String FILE_URL_PREFIX = "file:";
/** URL protocol for a file in the file system: "file" */
public static final String URL_PROTOCOL_FILE = "file";
/** URL protocol for an entry from a jar file: "jar" */
public static final String URL_PROTOCOL_JAR = "jar";
/** URL protocol for an entry from a zip file: "zip" */
public static final String URL_PROTOCOL_ZIP = "zip";
/** URL protocol for an entry from a JBoss jar file: "vfszip" */
public static final String URL_PROTOCOL_VFSZIP = "vfszip";
/** URL protocol for a JBoss VFS resource: "vfs" */
public static final String URL_PROTOCOL_VFS = "vfs";
/** URL protocol for an entry from a WebSphere jar file: "wsjar" */
public static final String URL_PROTOCOL_WSJAR = "wsjar";
/** URL protocol for an entry from an OC4J jar file: "code-source" */
public static final String URL_PROTOCOL_CODE_SOURCE = "code-source";
/**
* The relative path to the WEB-INF directory
*/
public static final String WEB_INF = "/WEB-INF";
/**
* The name of the Grails application directory
*/
public static final String GRAILS_APP_DIR = "grails-app";
/**
* The name of the Web app dir within Grails
*/
public static final String WEB_APP_DIR = "web-app";
/**
* The path to the views directory
*/
public static final String VIEWS_DIR_PATH = GRAILS_APP_DIR + "/views/";
/**
* The path to the views directory without a trailing slash
*/
public static final String VIEWS_DIR_PATH_NO_SLASH = GRAILS_APP_DIR + "/views";
public static final String DOMAIN_DIR_PATH = GRAILS_APP_DIR + "/domain/";
public static final String REGEX_FILE_SEPARATOR = "[\\\\/]"; // backslashes need escaping in regexes
/*
Domain path is always matched against the normalized File representation of an URL and
can therefore work with slashes as separators.
*/
public static Pattern DOMAIN_PATH_PATTERN = Pattern.compile(".+" + REGEX_FILE_SEPARATOR + GRAILS_APP_DIR + REGEX_FILE_SEPARATOR + "domain" + REGEX_FILE_SEPARATOR + "(.+)\\.(groovy|java)");
/*
This pattern will match any resource within a given directory inside grails-app
*/
public static Pattern RESOURCE_PATH_PATTERN = Pattern.compile(".+?" + REGEX_FILE_SEPARATOR + GRAILS_APP_DIR + REGEX_FILE_SEPARATOR + "(.+?)"+ REGEX_FILE_SEPARATOR +"(.+?\\.(groovy|java))");
public static Pattern SPRING_SCRIPTS_PATH_PATTERN = Pattern.compile(".+?" + REGEX_FILE_SEPARATOR + GRAILS_APP_DIR + REGEX_FILE_SEPARATOR + "conf"+ REGEX_FILE_SEPARATOR +"spring"+ REGEX_FILE_SEPARATOR +"(.+?\\.groovy)");
public static Pattern[] COMPILER_ROOT_PATTERNS = {
SPRING_SCRIPTS_PATH_PATTERN,
RESOURCE_PATH_PATTERN
};
/*
Resources are resolved against the platform specific path and must therefore obey the
specific File.separator.
*/
public static final Pattern GRAILS_RESOURCE_PATTERN_FIRST_MATCH;
public static final Pattern GRAILS_RESOURCE_PATTERN_SECOND_MATCH;
public static final Pattern GRAILS_RESOURCE_PATTERN_THIRD_MATCH;
public static final Pattern GRAILS_RESOURCE_PATTERN_FOURTH_MATCH;
public static final Pattern GRAILS_RESOURCE_PATTERN_FIFTH_MATCH;
public static final Pattern GRAILS_RESOURCE_PATTERN_SIXTH_MATCH;
public static final Pattern GRAILS_RESOURCE_PATTERN_SEVENTH_MATCH;
public static final Pattern GRAILS_RESOURCE_PATTERN_EIGHTH_MATCH;
public static final Pattern GRAILS_RESOURCE_PATTERN_NINTH_MATCH;
public static final Pattern GRAILS_RESOURCE_PATTERN_TENTH_MATCH;
public static final Pattern GRAILS_RESOURCE_PATTERN_ELEVENTH_MATCH;
static {
String fs = REGEX_FILE_SEPARATOR;
GRAILS_RESOURCE_PATTERN_FIRST_MATCH = Pattern.compile(createGrailsResourcePattern(fs, GRAILS_APP_DIR +fs+ "conf" +fs + "spring"));
GRAILS_RESOURCE_PATTERN_THIRD_MATCH = Pattern.compile(createGrailsResourcePattern(fs, GRAILS_APP_DIR +fs +"[\\w-]+"));
GRAILS_RESOURCE_PATTERN_SEVENTH_MATCH = Pattern.compile(createGrailsResourcePattern(fs, "src" + fs + "main" + fs + "java"));
GRAILS_RESOURCE_PATTERN_EIGHTH_MATCH = Pattern.compile(createGrailsResourcePattern(fs, "src" + fs + "main" + fs + "groovy"));
GRAILS_RESOURCE_PATTERN_NINTH_MATCH = Pattern.compile(createGrailsResourcePattern(fs, "src" + fs + "test" + fs + "groovy"));
GRAILS_RESOURCE_PATTERN_TENTH_MATCH = Pattern.compile(createGrailsResourcePattern(fs, "src" + fs + "test" + fs + "java"));
GRAILS_RESOURCE_PATTERN_ELEVENTH_MATCH = Pattern.compile(createGrailsResourcePattern(fs, "src" + fs + "test" + fs + "functional"));
GRAILS_RESOURCE_PATTERN_FIFTH_MATCH = Pattern.compile(createGrailsResourcePattern(fs, "grails-tests"));
fs = "/";
GRAILS_RESOURCE_PATTERN_SECOND_MATCH = Pattern.compile(createGrailsResourcePattern(fs, GRAILS_APP_DIR +fs+ "conf" +fs + "spring"));
GRAILS_RESOURCE_PATTERN_FOURTH_MATCH = Pattern.compile(createGrailsResourcePattern(fs, GRAILS_APP_DIR +fs +"[\\w-]+"));
GRAILS_RESOURCE_PATTERN_SIXTH_MATCH = Pattern.compile(createGrailsResourcePattern(fs, "grails-tests"));
}
public static final Pattern[] patterns = new Pattern[]{
GRAILS_RESOURCE_PATTERN_FIRST_MATCH,
GRAILS_RESOURCE_PATTERN_THIRD_MATCH,
GRAILS_RESOURCE_PATTERN_SEVENTH_MATCH,
GRAILS_RESOURCE_PATTERN_EIGHTH_MATCH,
GRAILS_RESOURCE_PATTERN_FOURTH_MATCH,
GRAILS_RESOURCE_PATTERN_FIFTH_MATCH,
GRAILS_RESOURCE_PATTERN_SIXTH_MATCH,
GRAILS_RESOURCE_PATTERN_NINTH_MATCH,
GRAILS_RESOURCE_PATTERN_TENTH_MATCH,
GRAILS_RESOURCE_PATTERN_ELEVENTH_MATCH
};
public static final Pattern[] grailsAppResourcePatterns = new Pattern[]{
GRAILS_RESOURCE_PATTERN_FIRST_MATCH,
GRAILS_RESOURCE_PATTERN_THIRD_MATCH,
GRAILS_RESOURCE_PATTERN_FOURTH_MATCH,
GRAILS_RESOURCE_PATTERN_FIFTH_MATCH,
GRAILS_RESOURCE_PATTERN_SIXTH_MATCH,
GRAILS_RESOURCE_PATTERN_ELEVENTH_MATCH
};
private static Map KNOWN_PATHS = new LinkedHashMap() {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return this.size() > 100;
}
};
private static Map KNOWN_DOMAIN_CLASSES = DefaultGroovyMethods.withDefault(new LinkedHashMap(){
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return this.size() > 100;
}
}, new Closure(GrailsResourceUtils.class) {
@Override
public Object call(Object... args) {
String path = args[0].toString();
return DOMAIN_PATH_PATTERN.matcher(path).find();
}
});
private static String createGrailsResourcePattern(String separator, String base) {
return ".+" + separator + base + separator + "(.+)\\.(groovy|java)$";
}
/**
* Checks whether the file referenced by the given url is a domain class
*
* @param url The URL instance
* @return true if it is a domain class
*/
public static boolean isDomainClass(URL url) {
if (url == null) return false;
return KNOWN_DOMAIN_CLASSES.get(url.getFile());
}
/**
* Extract the filename from the given path,
* e.g. "mypath/myfile.txt" -> "myfile.txt".
* @param path the file path (may be null
)
* @return the extracted filename, or null
if none
*/
public static String getFilename(String path) {
if (path == null) {
return null;
}
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);
}
/**
* Given an input class object, return a string which consists of the
* class's package name as a pathname, i.e., all dots ('.') are replaced by
* slashes ('/'). Neither a leading nor trailing slash is added. The result
* could be concatenated with a slash and the name of a resource and fed
* directly to ClassLoader.getResource()
. For it to be fed to
* Class.getResource
instead, a leading slash would also have
* to be prepended to the returned value.
* @param clazz the input class. A null
value or the default
* (empty) package will result in an empty string ("") being returned.
* @return a path which represents the package name
* @see ClassLoader#getResource
* @see Class#getResource
*/
public static String classPackageAsResourcePath(Class> clazz) {
if (clazz == null) {
return "";
}
String className = clazz.getName();
int packageEndIndex = className.lastIndexOf('.');
if (packageEndIndex == -1) {
return "";
}
String packageName = className.substring(0, packageEndIndex);
return packageName.replace('.', '/');
}
public static void useCachesIfNecessary(URLConnection con) {
con.setUseCaches(con.getClass().getName().startsWith("JNLP"));
}
/**
* Gets the class name of the specified Grails resource
*
* @param resource The Spring Resource
* @return The class name or null if the resource is not a Grails class
*/
public static String getClassName(Resource resource) {
try {
return getClassName(resource.getFile().getAbsolutePath());
}
catch (IOException e) {
return null;
}
}
/**
* Returns the class name for a Grails resource.
*
* @param path The path to check
* @return The class name or null if it doesn't exist
*/
public static String getClassName(String path) {
for (Pattern pattern : patterns) {
Matcher m = pattern.matcher(path);
if (m.find()) {
return m.group(1).replaceAll("[/\\\\]", ".");
}
}
return null;
}
/**
* Returns the class name for a compiled class file
*
* @param path The path to check
* @return The class name or null if it doesn't exist
*/
public static String getClassNameForClassFile(String rootDir, String path) {
path = path.replace("/", ".");
path = path.replace('\\', '.');
path = path.substring(0, path.length() - CLASS_EXTENSION.length());
if (rootDir != null) {
path = path.substring(rootDir.length());
}
return path;
}
/**
* Resolve the given resource URL to a java.io.File
,
* i.e. to a file in the file system.
* @param resourceUrl the resource URL to resolve
* @param description a description of the original resource that
* the URL was created for (for example, a class path location)
* @return a corresponding File object
* @throws java.io.FileNotFoundException if the URL cannot be resolved to
* a file in the file system
*/
public static File getFile(URL resourceUrl, String description) throws FileNotFoundException {
if (!URL_PROTOCOL_FILE.equals(resourceUrl.getProtocol())) {
throw new FileNotFoundException(
description + " cannot be resolved to absolute file path " +
"because it does not reside in the file system: " + resourceUrl);
}
try {
return new File(toURI(resourceUrl).getSchemeSpecificPart());
}
catch (URISyntaxException ex) {
// Fallback for URLs that are not valid URIs (should hardly ever happen).
return new File(resourceUrl.getFile());
}
}
/**
* Determine whether the given URL points to a resource in a jar file,
* that is, has protocol "jar", "zip", "wsjar" or "code-source".
* "zip" and "wsjar" are used by BEA WebLogic Server and IBM WebSphere, respectively,
* but can be treated like jar files. The same applies to "code-source" URLs on Oracle
* OC4J, provided that the path contains a jar separator.
* @param url the URL to check
* @return whether the URL has been identified as a JAR URL
*/
public static boolean isJarURL(URL url) {
String protocol = url.getProtocol();
return (URL_PROTOCOL_JAR.equals(protocol) ||
URL_PROTOCOL_ZIP.equals(protocol) ||
URL_PROTOCOL_WSJAR.equals(protocol) ||
(URL_PROTOCOL_CODE_SOURCE.equals(protocol) && url.getPath().contains(JAR_URL_SEPARATOR)));
}
/**
* Resolve the given resource URI to a java.io.File
,
* i.e. to a file in the file system.
* @param resourceUri the resource URI to resolve
* @param description a description of the original resource that
* the URI was created for (for example, a class path location)
* @return a corresponding File object
* @throws FileNotFoundException if the URL cannot be resolved to
* a file in the file system
*/
public static File getFile(URI resourceUri, String description) throws FileNotFoundException {
if (!URL_PROTOCOL_FILE.equals(resourceUri.getScheme())) {
throw new FileNotFoundException(
description + " cannot be resolved to absolute file path " +
"because it does not reside in the file system: " + resourceUri);
}
return new File(resourceUri.getSchemeSpecificPart());
}
/**
* Resolve the given resource URI to a java.io.File
,
* i.e. to a file in the file system.
* @param resourceUri the resource URI to resolve
* @return a corresponding File object
* @throws FileNotFoundException if the URL cannot be resolved to
* a file in the file system
*/
public static File getFile(URI resourceUri) throws FileNotFoundException {
return getFile(resourceUri, "URI");
}
/**
* Create a URI instance for the given URL,
* replacing spaces with "%20" quotes first.
*
Furthermore, this method works on JDK 1.4 as well,
* in contrast to the URL.toURI()
method.
* @param url the URL to convert into a URI instance
* @return the URI instance
* @throws URISyntaxException if the URL wasn't a valid URI
* @see java.net.URL#toURI()
*/
public static URI toURI(URL url) throws URISyntaxException {
return toURI(url.toString());
}
/**
* Determine whether the given URL points to a resource in the file system,
* that is, has protocol "file" or "vfs".
* @param url the URL to check
* @return whether the URL has been identified as a file system URL
*/
public static boolean isFileURL(URL url) {
String protocol = url.getProtocol();
return (URL_PROTOCOL_FILE.equals(protocol) || protocol.startsWith(URL_PROTOCOL_VFS));
}
/**
* Apply the given relative path to the given path,
* assuming standard Java folder separation (i.e. "/" separators).
* @param path the path to start from (usually a full file path)
* @param relativePath the relative path to apply
* (relative to the full file path above)
* @return the full file path that results from applying the relative path
*/
public static String applyRelativePath(String path, String relativePath) {
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
if (separatorIndex != -1) {
String newPath = path.substring(0, separatorIndex);
if (!relativePath.startsWith(FOLDER_SEPARATOR)) {
newPath += FOLDER_SEPARATOR;
}
return newPath + relativePath;
}
return relativePath;
}
/**
* Normalize the path by suppressing sequences like "path/.." and
* inner simple dots.
*
The result is convenient for path comparison. For other uses,
* notice that Windows separators ("\") are replaced by simple slashes.
* @param path the original path
* @return the normalized path
*/
public static String cleanPath(String path) {
if (path == null) {
return null;
}
String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);
// Strip prefix from path to analyze, to not treat it as part of the
// first path element. This is necessary to correctly parse paths like
// "file:core/../core/io/Resource.class", where the ".." should just
// strip the first "core" directory while keeping the "file:" prefix.
int prefixIndex = pathToUse.indexOf(":");
String prefix = "";
if (prefixIndex != -1) {
prefix = pathToUse.substring(0, prefixIndex + 1);
pathToUse = pathToUse.substring(prefixIndex + 1);
}
if (pathToUse.startsWith(FOLDER_SEPARATOR)) {
prefix = prefix + FOLDER_SEPARATOR;
pathToUse = pathToUse.substring(1);
}
String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);
List pathElements = new LinkedList();
int tops = 0;
for (int i = pathArray.length - 1; i >= 0; i--) {
String element = pathArray[i];
if (CURRENT_PATH.equals(element)) {
// Points to current directory - drop it.
}
else if (TOP_PATH.equals(element)) {
// Registering top path found.
tops++;
}
else {
if (tops > 0) {
// Merging path element with element corresponding to top path.
tops--;
}
else {
// Normal path element found.
pathElements.add(0, element);
}
}
}
// Remaining top paths need to be retained.
for (int i = 0; i < tops; i++) {
pathElements.add(0, TOP_PATH);
}
return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);
}
private static String collectionToDelimitedString(Collection> coll, String delim) {
return collectionToDelimitedString(coll, delim, "", "");
}
private static String collectionToDelimitedString(Collection> coll, String delim, String prefix, String suffix) {
if (coll == null || coll.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
Iterator> it = coll.iterator();
while (it.hasNext()) {
sb.append(prefix).append(it.next()).append(suffix);
if (it.hasNext()) {
sb.append(delim);
}
}
return sb.toString();
}
/**
* Take a String which is a delimited list and convert it to a String array.
* A single delimiter can consists of more than one character: It will still
* be considered as single delimiter string, rather than as bunch of potential
* delimiter characters - in contrast to tokenizeToStringArray
.
* @param str the input String
* @param delimiter the delimiter between elements (this is a single delimiter,
* rather than a bunch individual delimiter characters)
* @return an array of the tokens in the list
*/
private static String[] delimitedListToStringArray(String str, String delimiter) {
return delimitedListToStringArray(str, delimiter, null);
}
/**
* Take a String which is a delimited list and convert it to a String array.
*
A single delimiter can consists of more than one character: It will still
* be considered as single delimiter string, rather than as bunch of potential
* delimiter characters - in contrast to tokenizeToStringArray
.
* @param str the input String
* @param delimiter the delimiter between elements (this is a single delimiter,
* rather than a bunch individual delimiter characters)
* @param charsToDelete a set of characters to delete. Useful for deleting unwanted
* line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a String.
* @return an array of the tokens in the list
*/
private static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {
if (str == null) {
return new String[0];
}
if (delimiter == null) {
return new String[] {str};
}
List result = new ArrayList();
if ("".equals(delimiter)) {
for (int i = 0; i < str.length(); i++) {
result.add(deleteAny(str.substring(i, i + 1), charsToDelete));
}
}
else {
int pos = 0;
int delPos;
while ((delPos = str.indexOf(delimiter, pos)) != -1) {
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
pos = delPos + delimiter.length();
}
if (str.length() > 0 && pos <= str.length()) {
// Add rest of String, but not in case of empty input.
result.add(deleteAny(str.substring(pos), charsToDelete));
}
}
return toStringArray(result);
}
private static String[] toStringArray(Collection collection) {
if (collection == null) {
return null;
}
return collection.toArray(new String[collection.size()]);
}
private static String deleteAny(String inString, String charsToDelete) {
if (!hasLength(inString) || !hasLength(charsToDelete)) {
return inString;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < inString.length(); i++) {
char c = inString.charAt(i);
if (charsToDelete.indexOf(c) == -1) {
sb.append(c);
}
}
return sb.toString();
}
/**
* Replace all occurences of a substring within a string with
* another string.
* @param inString String to examine
* @param oldPattern String to replace
* @param newPattern String to insert
* @return a String with the replacements
*/
private static String replace(String inString, String oldPattern, String newPattern) {
if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) {
return inString;
}
StringBuilder sb = new StringBuilder();
int pos = 0; // our position in the old string
int index = inString.indexOf(oldPattern);
// the index of an occurrence we've found, or -1
int patLen = oldPattern.length();
while (index >= 0) {
sb.append(inString.substring(pos, index));
sb.append(newPattern);
pos = index + patLen;
index = inString.indexOf(oldPattern, pos);
}
sb.append(inString.substring(pos));
// remember to append any characters to the right of a match
return sb.toString();
}
private static boolean hasLength(CharSequence str) {
return (str != null && str.length() > 0);
}
/**
* Extract the URL for the actual jar file from the given URL
* (which may point to a resource in a jar file or to a jar file itself).
* @param jarUrl the original URL
* @return the URL for the actual jar file
* @throws MalformedURLException if no valid jar file URL could be extracted
*/
public static URL extractJarFileURL(URL jarUrl) throws MalformedURLException {
String urlFile = jarUrl.getFile();
int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR);
if (separatorIndex != -1) {
String jarFile = urlFile.substring(0, separatorIndex);
try {
return new URL(jarFile);
}
catch (MalformedURLException ex) {
// Probably no protocol in original jar URL, like "jar:C:/mypath/myjar.jar".
// This usually indicates that the jar file resides in the file system.
if (!jarFile.startsWith("/")) {
jarFile = "/" + jarFile;
}
return new URL(FILE_URL_PREFIX + jarFile);
}
}
return jarUrl;
}
/**
* Create a URI instance for the given location String,
* replacing spaces with "%20" quotes first.
* @param location the location String to convert into a URI instance
* @return the URI instance
* @throws URISyntaxException if the location wasn't a valid URI
*/
public static URI toURI(String location) throws URISyntaxException {
return new URI(replace(location, " ", "%20"));
}
/**
* Checks whether the specified path is a Grails path.
*
* @param path The path to check
* @return true if it is a Grails path
*/
public static boolean isGrailsPath(String path) {
if(KNOWN_PATHS.containsKey(path)) {
return KNOWN_PATHS.get(path);
}
for (Pattern grailsAppResourcePattern : grailsAppResourcePatterns) {
Matcher m = grailsAppResourcePattern.matcher(path);
if (m.find()) {
KNOWN_PATHS.put(path, true);
return true;
}
}
KNOWN_PATHS.put(path, false);
return false;
}
/**
* Checks whether the specified path is a Grails path.
*
* @param path The path to check
* @return true if it is a Grails path
*/
public static boolean isProjectSource(String path) {
for (Pattern grailsAppResourcePattern : patterns) {
Matcher m = grailsAppResourcePattern.matcher(path);
if (m.find()) {
return true;
}
}
return false;
}
/**
* Checks whether the specified path is a Grails path.
*
* @param r The resoruce to check
* @return true if it is a Grails path
*/
public static boolean isProjectSource(Resource r) {
try {
String file = r.getURL().getFile();
return isProjectSource(file) || file.endsWith("GrailsPlugin.groovy");
}
catch (IOException e) {
return false;
}
}
/**
* Checks whether the specific resources is a Grails resource. A Grails resource is a Groovy or Java class under the grails-app directory
*
* @param r The resource to check
* @return True if it is a Grails resource
*/
public static boolean isGrailsResource(Resource r) {
try {
String file = r.getURL().getFile();
return isGrailsPath(file) || file.endsWith("GrailsPlugin.groovy");
}
catch (IOException e) {
return false;
}
}
public static Resource getViewsDir(Resource resource) {
if (resource == null) return null;
Resource appDir = getAppDir(resource);
if(appDir == null) return null;
return appDir.createRelative("views");
}
public static Resource getAppDir(Resource resource) {
if (resource == null) return null;
try {
File file = resource.getFile();
while(file != null && !file.getName().equals(GRAILS_APP_DIR)) {
file = file.getParentFile();
}
if (file != null) {
return new FileSystemResource(file.getAbsolutePath() + '/');
}
} catch (IOException e) {
}
try {
String url = resource.getURL().toString();
int i = url.lastIndexOf(GRAILS_APP_DIR);
if (i > -1) {
url = url.substring(0, i+10);
return new UrlResource(url + '/');
}
return null;
}
catch (MalformedURLException e) {
return null;
}
catch (IOException e) {
return null;
}
}
private static final Pattern PLUGIN_PATTERN = Pattern.compile(".+?(/plugins/.+?/"+GRAILS_APP_DIR+"/.+)");
/**
* Takes a Grails resource (one located inside the grails-app dir) and gets its relative path inside the WEB-INF directory
* when deployed.
*
* @param resource The Grails resource, which is a file inside the grails-app dir
* @return The relative URL of the file inside the WEB-INF dir at deployment time or null if it cannot be established
*/
public static String getRelativeInsideWebInf(Resource resource) {
if (resource == null) return null;
try {
String url = resource.getURL().toString();
int i = url.indexOf(WEB_INF);
if (i > -1) {
return url.substring(i);
}
Matcher m = PLUGIN_PATTERN.matcher(url);
if (m.find()) {
return WEB_INF + m.group(1);
}
i = url.lastIndexOf(GRAILS_APP_DIR);
if (i > -1) {
return WEB_INF + "/" + url.substring(i);
}
}
catch (IOException e) {
return null;
}
return null;
}
private static final Pattern PLUGIN_RESOURCE_PATTERN = Pattern.compile(".+?/(plugins/.+?)/"+GRAILS_APP_DIR+"/.+");
/**
* Retrieves the static resource path for the given Grails resource artifact (controller/taglib etc.)
*
* @param resource The Resource
* @param contextPath The additonal context path to prefix
* @return The resource path
*/
public static String getStaticResourcePathForResource(Resource resource, String contextPath) {
if (contextPath == null) contextPath = "";
if (resource == null) return contextPath;
String url;
try {
url = resource.getURL().toString();
}
catch (IOException e) {
return contextPath;
}
Matcher m = PLUGIN_RESOURCE_PATTERN.matcher(url);
if (m.find()) {
return (contextPath.length() > 0 ? contextPath + "/" : "") + m.group(1);
}
return contextPath;
}
/**
* Get the path relative to an artefact folder under grails-app i.e:
*
* Input: /usr/joe/project/grails-app/conf/BootStrap.groovy
* Output: BootStrap.groovy
*
* Input: /usr/joe/project/grails-app/domain/com/mystartup/Book.groovy
* Output: com/mystartup/Book.groovy
*
* @param path The path to evaluate
* @return The path relative to the root folder grails-app
*/
public static String getPathFromRoot(String path) {
for (Pattern COMPILER_ROOT_PATTERN : COMPILER_ROOT_PATTERNS) {
Matcher m = COMPILER_ROOT_PATTERN.matcher(path);
if (m.find()) {
return m.group(m.groupCount()-1);
}
}
return null;
}
/**
* Gets the path relative to the project base directory.
*
* Input: /usr/joe/project/grails-app/conf/BootStrap.groovy
* Output: grails-app/conf/BootStrap.groovy
*
* @param path The path
* @return The path relative to the base directory or null if it can't be established
*/
public static String getPathFromBaseDir(String path) {
int i = path.indexOf("grails-app/");
if(i > -1 ) {
return path.substring(i + 11);
}
else {
try {
File baseDir = BuildSettings.BASE_DIR;
String basePath = baseDir != null ? baseDir.getCanonicalPath() : null;
if(basePath != null) {
String canonicalPath = new File(path).getCanonicalPath();
return canonicalPath.substring(basePath.length()+1);
}
} catch (IOException e) {
// ignore
}
}
return null;
}
/**
* Takes a file path and returns the name of the folder under grails-app i.e:
*
* Input: /usr/joe/project/grails-app/domain/com/mystartup/Book.groovy
* Output: domain
*
* @param path The path
* @return The domain or null if not known
*/
public static String getArtefactDirectory(String path) {
if (path != null) {
final Matcher matcher = RESOURCE_PATH_PATTERN.matcher(path);
if (matcher.find()) {
return matcher.group(1);
}
}
return null;
}
/**
* Takes any number of Strings and appends them into a uri, making
* sure that a forward slash is inserted between each piece and
* making sure that no duplicate slashes are in the uri
*
*
* Input: ""
* Output: ""
*
* Input: "/alpha", "/beta", "/gamma"
* Output: "/alpha/beta/gamma
*
* Input: "/alpha/, "/beta/", "/gamma"
* Output: "/alpha/beta/gamma
*
* Input: "/alpha/", "/beta/", "/gamma/"
* Output "/alpha/beta/gamma/
*
* Input: "alpha", "beta", "gamma"
* Output: "alpha/beta/gamma
*
*
* @param pieces Strings to concatenate together into a uri
* @return a uri
*/
public static String appendPiecesForUri(String... pieces) {
if (pieces==null || pieces.length==0) return "";
// join parts && strip double slashes
StringBuilder builder = new StringBuilder(16 * pieces.length);
char previous = 0;
for (int i=0; i < pieces.length;i++) {
String piece = pieces[i];
if (piece != null && piece.length() > 0) {
for (int j=0, maxlen=piece.length();j < maxlen;j++) {
char current=piece.charAt(j);
if (!(previous=='/' && current=='/')) {
builder.append(current);
previous = current;
}
}
if (i + 1 < pieces.length && previous != '/') {
builder.append('/');
previous='/';
}
}
}
return builder.toString();
}
@SuppressWarnings("unchecked")
public static Object instantiateFromConfig(ConfigObject config, String configKey, String defaultClassName)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, LinkageError {
return instantiateFromFlatConfig(config.flatten(), configKey, defaultClassName);
}
public static Object instantiateFromFlatConfig(Map flatConfig, String configKey, String defaultClassName)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, LinkageError {
String className = defaultClassName;
Object configName = flatConfig.get(configKey);
if (configName instanceof CharSequence) {
className = configName.toString();
}
return forName(className, DefaultResourceLoader.getDefaultClassLoader()).newInstance();
}
private static Class> forName(String className, ClassLoader defaultClassLoader) throws ClassNotFoundException {
return defaultClassLoader.loadClass(className);
}
}