/*
* Copyright 2008-2010 the T2 Project ant the Others.
*
* 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.t2framework.confeito.spi.impl;
import java.io.File;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarFile;
import org.t2framework.confeito.Constants;
import org.t2framework.confeito.spi.PageRegistrationCommand;
import org.t2framework.confeito.spi.PageRegistrationHandler;
import org.t2framework.confeito.urltemplate.UrlTemplate;
import org.t2framework.confeito.util.Assertion;
import org.t2framework.confeito.util.JarFileUtil;
import org.t2framework.confeito.util.Logger;
import org.t2framework.confeito.util.ResourceUtil;
import org.t2framework.confeito.util.Traversal;
import org.t2framework.confeito.util.URLUtil;
import org.t2framework.confeito.util.ZipFileUtil;
import org.t2framework.confeito.util.Traversal.ClassHandler;
/**
* Default implementation of {@link PageRegistrationHandler}. This
* implementation provides several {@link Strategy} inside, and each of these is
* provided by URL prefix such as file, jar, zip.
*
* @author shot
*
*/
public class DefaultPageRegistrationHandlerImpl implements
PageRegistrationHandler {
/**
* The logger for this class.
*/
protected static Logger logger = Logger
.getLogger(DefaultPageRegistrationHandlerImpl.class);
/**
* Strategies map.Key is url prefix, value is {@link Strategy} itself.
*/
protected Map<String, Strategy> strategies = new HashMap<String, Strategy>();
/**
* {@link ClassHandler} for processing page class registration.
*/
protected ClassHandler classHandler;
/**
* Construct this class.Given {@link ClassHandler} must not be null.
*
* @param classHandler
*/
public DefaultPageRegistrationHandlerImpl(ClassHandler classHandler) {
this.classHandler = Assertion.notNull(classHandler);
setupStrategies();
}
/**
* Setting up basic strategies.Developer can add later using
* {@link DefaultPageRegistrationHandlerImpl#addStrategy(String, Strategy)}.
*/
protected void setupStrategies() {
addStrategy(Constants.URL_PROTOCOL_FILE, new FileSystemStrategy());
addStrategy(Constants.URL_PROTOCOL_JAR, new JarFileStrategy());
addStrategy(Constants.URL_PROTOCOL_WSJAR, new JarFileStrategy());
addStrategy(Constants.URL_PROTOCOL_ZIP, new ZipFileStrategy());
addStrategy(Constants.URL_PROTOCOL_VFSZIP, new VfsZipFileStrategy());
addStrategy(Constants.URL_PROTOCOL_CODE_SOURCE,
new CodeSourceFileStrategy());
}
/**
* Add customize {@link Strategy}.
*
* @param protocol
* @param strategy
*/
public void addStrategy(String protocol, Strategy strategy) {
strategies.put(protocol, strategy);
}
@Override
public PageRegistrationCommand handle(String rootPackage,
Map<Class<?>, UrlTemplate> classCache) {
final String path = rootPackage.replace('.', '/');
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = ResourceUtil.getResources(loader, path);
while (resources.hasMoreElements()) {
final URL u = resources.nextElement();
final String externalForm = u.toExternalForm();
String filepath = u.getFile();
filepath = URLUtil.decode(filepath, "UTF8");
logger.log("ITDT0035", new Object[] { rootPackage, externalForm });
if (filepath.startsWith(Constants.FILE_URL_PREFIX)) {
filepath = filepath.substring(Constants.FILE_URL_PREFIX_LENGTH);
}
if (0 < filepath.indexOf("!")) {
filepath = filepath.substring(0, filepath.indexOf("!"));
}
final File file = new File(filepath);
if (file.isDirectory()) {
loadFromFilesystem(path, file, u);
} else {
loadFromJar(path, file, u);
}
}
return PageRegistrationCommand.STOP;
}
protected void loadFromFilesystem(String packagename, File file, URL url) {
strategies.get("file")
.handle(packagename, file, url, this.classHandler);
}
protected void loadFromJar(String packagename, File file, URL url) {
String protocol = url.getProtocol();
Strategy strategy = strategies.get(protocol);
if (strategy == null) {
throw new NullPointerException();
}
strategy.handle(packagename, file, url, this.classHandler);
}
/**
*
* <#if locale="en">
* <p>
* Internal strategy for page registration.
* </p>
* <#else>
* <p>
* </p>
* </#if>
*
* @author shot
*
*/
public static interface Strategy {
void handle(String packagename, File root, URL url, ClassHandler handler);
}
/**
* {@link Strategy} for file.
*
* @author shot
*
*/
protected static class FileSystemStrategy implements Strategy {
@Override
public void handle(String packagename, File root, URL url,
ClassHandler handler) {
File[] files = root.listFiles();
StringBuilder builder = null;
if (files == null) {
return;
}
for (File f : files) {
builder = new StringBuilder(100);
final String name = f.getName();
builder.append(packagename).append("/").append(name);
final String packageOrClass = (root == null ? name : builder
.toString());
if (f.isDirectory()) {
handle(packageOrClass, f, url, handler);
} else if (name.endsWith(Constants.CLASS_SUFFIX)) {
final String className = packageOrClass.substring(
0,
packageOrClass.length()
- Constants.CLASS_SUFFIX.length()).replace(
'/', '.');
final int pos = className.lastIndexOf('.');
final String shortClassName = (pos == -1) ? className
: className.substring(pos + 1);
handler.processClass(packagename, shortClassName);
}
}
}
}
/**
* Strategy for jar.
*
* @author shot
*
*/
protected static class JarFileStrategy implements Strategy {
@Override
public void handle(String packagename, File root, URL url,
ClassHandler handler) {
final JarFile jarFile = createJarFile(url);
Traversal.traverseFromJarFile(jarFile, handler);
}
protected JarFile createJarFile(final URL url) {
return JarFileUtil.toJarFile(url);
}
}
/**
* Strategy for zip.
*
* @author shot
*
*/
protected static class ZipFileStrategy implements Strategy {
@Override
public void handle(String packagename, File root, URL url,
ClassHandler handler) {
final JarFile jarFile = createJarFile(url);
Traversal.traverseFromJarFile(jarFile, handler);
}
protected JarFile createJarFile(final URL url) {
final String jarFileName = ZipFileUtil.toZipFilePath(url);
return JarFileUtil.create(new File(jarFileName));
}
}
/**
* Strategy for code-source.
*
* @author shot
*
*/
protected static class CodeSourceFileStrategy implements Strategy {
@Override
public void handle(String packagename, File root, URL url,
ClassHandler handler) {
final JarFile jarFile = createJarFile(url);
Traversal.traverseFromJarFile(jarFile, handler);
}
protected JarFile createJarFile(final URL url) {
final URL jarUrl = URLUtil.create("jar:file:" + url.getPath());
return JarFileUtil.toJarFile(jarUrl);
}
}
/**
* Strategy for vfs-zip.
*
* @author shot
*
*/
protected static class VfsZipFileStrategy implements Strategy {
@Override
public void handle(String packagename, File root, URL url,
ClassHandler handler) {
final JarFile jarFile = createJarFile(url);
Traversal.traverseFromJarFile(jarFile, handler);
}
protected JarFile createJarFile(final URL url) {
final String path = url.getPath();
String pathUp = path.toUpperCase();
String warUp = Constants.WAR_SUFFIX.toUpperCase();
String warPath = path.substring(0, pathUp.indexOf(warUp)
+ Constants.WAR_SUFFIX.length());
return JarFileUtil.create(warPath);
}
}
}