/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* StandaloneJarResourceAccess.java
* Created: Jun 5, 2007
* By: Joseph Wong
*/
package org.openquark.cal.internal.runtime.lecc;
import java.io.InputStream;
import java.util.Locale;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.runtime.ResourceAccess;
import org.openquark.cal.services.LocaleUtilities;
/**
* An implementation of the {@link ResourceAccess} interface for use in a standalone jar. The user resources
* are packaged up into the jar, using the same directory structure as in the StandardVault or in a Car/Car-jar.
*
* Note that StandaloneJarBuilder is the builder that creates standalone jars.
*
* @author Joseph Wong
*/
public final class StandaloneJarResourceAccess implements ResourceAccess {
/** The disambiguating prefix in front of a locale specifier in a file name. */
private static final String USER_RESOURCE_LOCALE_PREFIX = "_";
/** The base folder for this kind of resource. */
private static final String baseUserResourceFolder = "CAL_Resources";
/**
* The class loader that loaded the standalone JAR, which can then be used to access resources
* in the JAR. (needed because the class loader which loaded this class may be an <i>ancestor</i>
* of the one which loaded the standalone JAR (e.g. the bootstrap class loader), and thus may
* not have access to resources necessary for the standalone JAR to run).
*
* Cannot be null.
*/
private final ClassLoader standaloneJarClassLoader;
/**
* Constructs an instance of this class.
* @param standaloneJarClassLoader the class loader that loaded the standalone JAR. Cannot be null.
*/
public StandaloneJarResourceAccess(final ClassLoader standaloneJarClassLoader) {
if (standaloneJarClassLoader == null) {
throw new NullPointerException("standaloneJarClassLoader cannot be null");
}
this.standaloneJarClassLoader = standaloneJarClassLoader;
}
/**
* {@inheritDoc}
*/
public InputStream getUserResource(final String moduleNameAsString, final String name, final String extension, final Locale locale) {
final ModuleName moduleName = ModuleName.make(moduleNameAsString);
// ClassLoader.getResourceAsStream() expects a path without the leading slash.
final String pathString = getResourcePath(moduleName, name, extension, locale);
return standaloneJarClassLoader.getResourceAsStream(pathString);
}
/**
* An inlined implementation of {@code UserResourcePathMapper.getResourcePath}.
*
* Returns the base path to a resource representing a given feature.
*
* @param moduleName the name of the module associated with the user resource.
* @param name the name of the resource, not including any file extensions. Cannot contain the character '_'.
* @param extension the file extension for the user resource.
* @param locale the locale for which the resource is to be fetched.
* @return the corresponding resource path without a leading slash.
*/
private String getResourcePath(final ModuleName moduleName, final String name, final String extension, final Locale locale) {
if (name.indexOf(USER_RESOURCE_LOCALE_PREFIX) != -1) {
throw new IllegalArgumentException("The locale identifier prefix '" + USER_RESOURCE_LOCALE_PREFIX + "' cannot appear in the name of a user resource.");
}
String fileName = name;
if (!LocaleUtilities.isInvariantLocale(locale)) {
fileName += USER_RESOURCE_LOCALE_PREFIX + LocaleUtilities.localeToCanonicalString(locale);
}
if (extension.length() > 0) {
fileName += "." + extension;
}
return getModuleResourcePath(moduleName) + '/' + fileName;
}
/**
* An inlined implementation of {@code UserResourcePathMapper.getModuleResourcePath}.
*
* Returns the base path to the file or folder which contains the resources for the given module.
*
* @param moduleName the name of the module.
* @return the path (without leading slash) under which the resources corresponding to the module should be located.
*/
private String getModuleResourcePath(final ModuleName moduleName) {
String[] components = moduleName.toSourceText().split("\\.");
int nComponents = components.length;
StringBuilder result = new StringBuilder(baseUserResourceFolder);
for (int i = 0; i < nComponents; i++) {
result.append('/').append(getFileSystemName(components[i]));
}
return result.toString();
}
/**
* An inlined implementation of {@code FileSystemResourceHelper.getFileSystemName}.
*
* Converts a desired filename into an a name for use in the file system by
* preceding all capital letters with hyphens. This is needed because not
* all filesystems are case-sensitive (ie: Windows), but CAL is case sensitive.
*
* @param name the name to convert.
* @return the valid file name.
*/
private String getFileSystemName(final String name) {
StringBuilder fileName = new StringBuilder();
int length = name.length();
for (int i = 0; i < length; i++) {
char c = name.charAt(i);
if (Character.isUpperCase(c)) {
fileName.append("-" + c);
} else {
fileName.append(c);
}
}
return fileName.toString();
}
}