2009-03-18 07:22:37 +08:00
|
|
|
package org.broadinstitute.sting.gatk;
|
|
|
|
|
|
2009-03-19 01:32:24 +08:00
|
|
|
import net.sf.functionalj.reflect.StdReflect;
|
|
|
|
|
import net.sf.functionalj.reflect.JdkStdReflect;
|
|
|
|
|
import net.sf.functionalj.FunctionN;
|
|
|
|
|
import net.sf.functionalj.Functions;
|
|
|
|
|
|
2009-03-25 08:12:00 +08:00
|
|
|
import java.lang.reflect.Field;
|
2009-03-18 07:22:37 +08:00
|
|
|
import java.lang.reflect.Modifier;
|
|
|
|
|
import java.io.File;
|
2009-03-19 01:32:24 +08:00
|
|
|
import java.io.FilenameFilter;
|
2009-03-18 07:22:37 +08:00
|
|
|
import java.io.FileInputStream;
|
|
|
|
|
import java.io.IOException;
|
2009-03-19 01:32:24 +08:00
|
|
|
import java.net.URL;
|
|
|
|
|
import java.net.URLClassLoader;
|
2009-03-18 07:22:37 +08:00
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.jar.JarEntry;
|
|
|
|
|
import java.util.jar.JarInputStream;
|
|
|
|
|
|
|
|
|
|
import org.broadinstitute.sting.gatk.walkers.Walker;
|
2009-03-27 04:45:27 +08:00
|
|
|
import org.broadinstitute.sting.gatk.walkers.WalkerName;
|
2009-03-25 08:12:00 +08:00
|
|
|
import org.broadinstitute.sting.utils.cmdLine.Argument;
|
2009-03-27 21:27:04 +08:00
|
|
|
import org.apache.log4j.Logger;
|
2009-03-18 07:22:37 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Created by IntelliJ IDEA.
|
|
|
|
|
* User: hanna
|
|
|
|
|
* Date: Mar 17, 2009
|
|
|
|
|
* Time: 3:14:28 PM
|
|
|
|
|
* To change this template use File | Settings | File Templates.
|
|
|
|
|
*/
|
|
|
|
|
public class WalkerManager {
|
|
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
/**
|
|
|
|
|
* our log, which we want to capture anything from this class
|
|
|
|
|
*/
|
|
|
|
|
private static Logger logger = Logger.getLogger(WalkerManager.class);
|
|
|
|
|
|
|
|
|
|
private Map<String, Class> walkers;
|
|
|
|
|
|
|
|
|
|
public WalkerManager(String pluginDirectory) {
|
2009-03-18 07:22:37 +08:00
|
|
|
try {
|
2009-03-27 04:45:27 +08:00
|
|
|
List<Class> walkerCandidates = new ArrayList<Class>();
|
2009-03-19 01:32:24 +08:00
|
|
|
|
2009-03-20 06:12:25 +08:00
|
|
|
// Load all classes that live in this jar.
|
2009-03-24 11:56:49 +08:00
|
|
|
final File location = getThisLocation();
|
2009-03-27 21:27:04 +08:00
|
|
|
walkerCandidates.addAll(loadClassesFromLocation(location));
|
2009-03-19 01:32:24 +08:00
|
|
|
|
2009-03-20 06:12:25 +08:00
|
|
|
// Load all classes that live in the extension path.
|
2009-03-27 21:27:04 +08:00
|
|
|
if (pluginDirectory == null)
|
2009-03-24 11:56:49 +08:00
|
|
|
pluginDirectory = location.getParent() + File.separator + "walkers";
|
2009-03-27 21:27:04 +08:00
|
|
|
logger.info("plugin directory: " + pluginDirectory);
|
2009-03-24 11:56:49 +08:00
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
File extensionPath = new File(pluginDirectory);
|
|
|
|
|
if (extensionPath.exists()) {
|
|
|
|
|
List<String> filesInPath = findFilesInPath(extensionPath, "", "class", false);
|
|
|
|
|
walkerCandidates.addAll(loadExternalClasses(extensionPath, filesInPath));
|
2009-03-24 11:56:49 +08:00
|
|
|
}
|
2009-03-19 01:32:24 +08:00
|
|
|
|
2009-03-27 04:45:27 +08:00
|
|
|
walkerCandidates = filterWalkers(walkerCandidates);
|
2009-03-18 07:22:37 +08:00
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
if (walkerCandidates.isEmpty())
|
|
|
|
|
throw new RuntimeException("No walkers were found.");
|
2009-03-18 07:22:37 +08:00
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
walkers = createWalkerDatabase(walkerCandidates);
|
2009-03-18 07:22:37 +08:00
|
|
|
}
|
|
|
|
|
// IOExceptions here are suspect; they indicate that the WalkerManager can't open its containing jar.
|
|
|
|
|
// Wrap in a RuntimeException.
|
2009-03-27 21:27:04 +08:00
|
|
|
catch (IOException ex) {
|
2009-03-18 07:22:37 +08:00
|
|
|
throw new RuntimeException(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Does a walker with the given name exist?
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-18 07:22:37 +08:00
|
|
|
* @param walkerName Name of the walker for which to search.
|
|
|
|
|
* @return True if the walker exists, false otherwise.
|
|
|
|
|
*/
|
|
|
|
|
public boolean doesWalkerExist(String walkerName) {
|
|
|
|
|
return walkers.containsKey(walkerName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gets a walker with the given name, or null if no walker exists.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-18 07:22:37 +08:00
|
|
|
* @param walkerName Name of the walker to retrieve.
|
|
|
|
|
* @return The walker object if found; null otherwise.
|
|
|
|
|
*/
|
2009-03-27 04:45:27 +08:00
|
|
|
public Walker createWalkerByName(String walkerName)
|
|
|
|
|
throws InstantiationException, IllegalAccessException {
|
|
|
|
|
Class walker = walkers.get(walkerName);
|
2009-03-27 21:27:04 +08:00
|
|
|
return (Walker) walker.newInstance();
|
2009-03-27 04:45:27 +08:00
|
|
|
}
|
|
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
public Class getWalkerClassByName(String walkerName) {
|
2009-03-27 04:45:27 +08:00
|
|
|
return walkers.get(walkerName);
|
2009-03-18 07:22:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determines which jar file contains the WalkerManager class.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-18 07:22:37 +08:00
|
|
|
* @return Jar file containing the WalkerManager class.
|
|
|
|
|
*/
|
2009-03-24 11:56:49 +08:00
|
|
|
private File getThisLocation() throws IOException {
|
2009-03-18 07:22:37 +08:00
|
|
|
try {
|
2009-03-24 11:56:49 +08:00
|
|
|
java.net.URI locationURI = getClass().getProtectionDomain().getCodeSource().getLocation().toURI();
|
2009-03-27 21:27:04 +08:00
|
|
|
return new File(locationURI);
|
2009-03-18 07:22:37 +08:00
|
|
|
}
|
2009-03-27 21:27:04 +08:00
|
|
|
catch (java.net.URISyntaxException ex) {
|
2009-03-18 07:22:37 +08:00
|
|
|
// a URISyntaxException here must be an IO error; wrap as such.
|
|
|
|
|
throw new IOException(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-24 11:56:49 +08:00
|
|
|
/**
|
|
|
|
|
* Load classes internal to the classpath from an arbitrary location.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-24 11:56:49 +08:00
|
|
|
* @param location Location from which to load classes.
|
|
|
|
|
* @return List of classes.
|
|
|
|
|
* @throws IOException Problem occurred reading classes.
|
|
|
|
|
*/
|
2009-03-27 21:27:04 +08:00
|
|
|
private List<Class> loadClassesFromLocation(File location)
|
|
|
|
|
throws IOException {
|
|
|
|
|
if (location.getAbsolutePath().endsWith(".jar"))
|
|
|
|
|
return loadClassesFromJar(location);
|
2009-03-24 11:56:49 +08:00
|
|
|
else {
|
2009-03-27 21:27:04 +08:00
|
|
|
List<String> classFileNames = findFilesInPath(location, "", "class", true);
|
|
|
|
|
return loadInternalClasses(classFileNames);
|
2009-03-24 11:56:49 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-18 07:22:37 +08:00
|
|
|
/**
|
|
|
|
|
* Loads concrete classes from a jar which are both in the same package or 'sub-package' of baseClass,
|
|
|
|
|
* and which extend from baseClass.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-18 07:22:37 +08:00
|
|
|
* @param jarFile The jar file to search.
|
|
|
|
|
* @return A list of classes derived from baseClass.
|
|
|
|
|
*/
|
2009-03-20 06:12:25 +08:00
|
|
|
private List<Class> loadClassesFromJar(final File jarFile)
|
2009-03-18 07:22:37 +08:00
|
|
|
throws IOException {
|
|
|
|
|
List<Class> subclasses = new ArrayList<Class>();
|
|
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile));
|
2009-03-18 07:22:37 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
JarEntry jarEntry = jarInputStream.getNextJarEntry();
|
|
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
while (jarEntry != null) {
|
2009-03-18 07:22:37 +08:00
|
|
|
String jarEntryName = jarEntry.getName();
|
2009-03-27 21:27:04 +08:00
|
|
|
if (jarEntryName.endsWith(".class")) {
|
2009-03-24 11:56:49 +08:00
|
|
|
String className = fileNameToClassName(jarEntryName);
|
2009-03-27 21:27:04 +08:00
|
|
|
subclasses.add(Class.forName(className));
|
2009-03-18 07:22:37 +08:00
|
|
|
}
|
|
|
|
|
jarEntry = jarInputStream.getNextJarEntry();
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-03-27 21:27:04 +08:00
|
|
|
catch (ClassNotFoundException ex) {
|
2009-03-18 07:22:37 +08:00
|
|
|
// A ClassNotFoundException here must be an IO error; wrap as such.
|
|
|
|
|
throw new IOException(ex);
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
jarInputStream.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return subclasses;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-19 01:32:24 +08:00
|
|
|
/**
|
2009-03-24 11:56:49 +08:00
|
|
|
* Loads a list of classes currently on the classpath.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-24 11:56:49 +08:00
|
|
|
* @param classFileNames List of files representing classes.
|
|
|
|
|
* @return class objects.
|
|
|
|
|
* @throws IOException Unable to open any of the found classes.
|
|
|
|
|
*/
|
2009-03-27 21:27:04 +08:00
|
|
|
private List<Class> loadInternalClasses(List<String> classFileNames)
|
2009-03-24 11:56:49 +08:00
|
|
|
throws IOException {
|
|
|
|
|
List<Class> internalClasses = new ArrayList<Class>();
|
|
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
for (String classFileName : classFileNames) {
|
|
|
|
|
String className = fileNameToClassName(classFileName);
|
2009-03-24 11:56:49 +08:00
|
|
|
try {
|
2009-03-27 21:27:04 +08:00
|
|
|
internalClasses.add(Class.forName(className));
|
2009-03-24 11:56:49 +08:00
|
|
|
}
|
2009-03-27 21:27:04 +08:00
|
|
|
catch (ClassNotFoundException ex) {
|
2009-03-24 11:56:49 +08:00
|
|
|
// A ClassNotFoundException here must be an IO error; wrap as such.
|
|
|
|
|
throw new IOException(ex);
|
2009-03-27 21:27:04 +08:00
|
|
|
}
|
2009-03-24 11:56:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return internalClasses;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load loose classes, external to the classloader, from the specified directory.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-19 01:32:24 +08:00
|
|
|
* @param path source path from which to load classes.
|
|
|
|
|
* @return A list of all loose classes contained in the path directory.
|
|
|
|
|
*/
|
2009-03-24 11:56:49 +08:00
|
|
|
private List<Class> loadExternalClasses(final File path, List<String> classFileNames)
|
2009-03-19 01:32:24 +08:00
|
|
|
throws IOException {
|
|
|
|
|
List<Class> subclasses = new ArrayList<Class>();
|
|
|
|
|
|
|
|
|
|
URL pathURL = path.toURI().toURL();
|
|
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
ClassLoader cl = new URLClassLoader(new URL[]{pathURL});
|
2009-03-19 01:32:24 +08:00
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
List<String> filesInPath = findFilesInPath(path, "", "class", false);
|
|
|
|
|
for (String file : filesInPath) {
|
|
|
|
|
String className = fileNameToClassName(file);
|
2009-03-19 01:32:24 +08:00
|
|
|
try {
|
|
|
|
|
subclasses.add(cl.loadClass(className));
|
|
|
|
|
}
|
2009-03-27 21:27:04 +08:00
|
|
|
catch (ClassNotFoundException ex) {
|
2009-03-19 01:32:24 +08:00
|
|
|
// Class not found from a list of classes just looked up is an IO error. Wrap and throw.
|
|
|
|
|
throw new IOException(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return subclasses;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-24 11:56:49 +08:00
|
|
|
/**
|
|
|
|
|
* Find the files in the given directory matching the given extension.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
|
|
|
|
* @param basePath Path to search.
|
2009-03-24 11:56:49 +08:00
|
|
|
* @param relativePrefix What directory should the given files be presented relative to?
|
2009-03-27 21:27:04 +08:00
|
|
|
* @param extension Extension for which to search.
|
|
|
|
|
* @param recursive Search recursively. Beware of symlinks!
|
2009-03-24 11:56:49 +08:00
|
|
|
* @return A list of files matching the specified criteria.
|
2009-03-27 21:27:04 +08:00
|
|
|
* TODO: Move to a utils class.
|
|
|
|
|
* TODO: Test recursive traversal in the presence of a symlink.
|
2009-03-24 11:56:49 +08:00
|
|
|
*/
|
|
|
|
|
private List<String> findFilesInPath(final File basePath, final String relativePrefix, final String extension, boolean recursive) {
|
2009-03-27 04:45:27 +08:00
|
|
|
List<String> filesInPath = new ArrayList<String>();
|
2009-03-24 11:56:49 +08:00
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
File[] contents = basePath.listFiles(new OrFilenameFilter(new DirectoryFilter(), new ExtensionFilter(extension)));
|
|
|
|
|
for (File content : contents) {
|
2009-03-24 11:56:49 +08:00
|
|
|
String relativeFileName = relativePrefix.trim().length() != 0 ?
|
2009-03-27 21:27:04 +08:00
|
|
|
relativePrefix + File.separator + content.getName() :
|
|
|
|
|
content.getName();
|
|
|
|
|
if (relativeFileName.endsWith(extension))
|
2009-03-24 11:56:49 +08:00
|
|
|
filesInPath.add(relativeFileName);
|
2009-03-27 21:27:04 +08:00
|
|
|
else if (content.isDirectory() && recursive)
|
|
|
|
|
filesInPath.addAll(findFilesInPath(content, relativeFileName, extension, recursive));
|
2009-03-24 11:56:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filesInPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert a filename of the form a/b/c.class to a.b.c. Makes no assurances about whether the
|
|
|
|
|
* class is valid on any classloader.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-24 11:56:49 +08:00
|
|
|
* @param fileName Filename to convert.
|
|
|
|
|
* @return classname represented by that file.
|
2009-03-27 21:27:04 +08:00
|
|
|
* TODO: Move to a utils class.
|
2009-03-24 11:56:49 +08:00
|
|
|
*/
|
2009-03-27 21:27:04 +08:00
|
|
|
private String fileNameToClassName(String fileName) {
|
|
|
|
|
return fileName.substring(0, fileName.lastIndexOf(".class")).replace('/', '.');
|
2009-03-24 11:56:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The following are general-purpose file selection filters.
|
|
|
|
|
* TODO: Move to a utils class.
|
|
|
|
|
*/
|
|
|
|
|
private class ExtensionFilter implements FilenameFilter {
|
|
|
|
|
private String extensionName = null;
|
2009-03-27 21:27:04 +08:00
|
|
|
|
|
|
|
|
public ExtensionFilter(String extensionName) {
|
|
|
|
|
this.extensionName = extensionName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean accept(File f, String s) {
|
|
|
|
|
return s.endsWith("." + extensionName);
|
|
|
|
|
}
|
2009-03-24 11:56:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class DirectoryFilter implements FilenameFilter {
|
2009-03-27 21:27:04 +08:00
|
|
|
public boolean accept(File f, String s) {
|
|
|
|
|
return new File(f, s).isDirectory();
|
|
|
|
|
}
|
2009-03-24 11:56:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class OrFilenameFilter implements FilenameFilter {
|
|
|
|
|
private FilenameFilter lhs = null, rhs = null;
|
2009-03-27 21:27:04 +08:00
|
|
|
|
|
|
|
|
public OrFilenameFilter(FilenameFilter lhs, FilenameFilter rhs) {
|
|
|
|
|
this.lhs = lhs;
|
|
|
|
|
this.rhs = rhs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean accept(File f, String s) {
|
|
|
|
|
return lhs.accept(f, s) || rhs.accept(f, s);
|
|
|
|
|
}
|
2009-03-24 11:56:49 +08:00
|
|
|
}
|
|
|
|
|
|
2009-03-19 01:32:24 +08:00
|
|
|
/**
|
|
|
|
|
* Given a list of classes, return a list of those classes which extend from the Walker base interface.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-19 01:32:24 +08:00
|
|
|
* @param classes Arbitrary list of classes.
|
|
|
|
|
* @return List of classes extending from Walker.
|
|
|
|
|
*/
|
|
|
|
|
private List<Class> filterWalkers(List<Class> classes) {
|
|
|
|
|
StdReflect reflect = new JdkStdReflect();
|
2009-03-27 21:27:04 +08:00
|
|
|
FunctionN<Boolean> filterFunc = reflect.instanceFunction(new ClassFilter(Walker.class), "filter", Class.class);
|
|
|
|
|
return Functions.findAll(filterFunc.f1(), classes);
|
2009-03-19 01:32:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A functor returning true for classes which extend from baseClass.
|
|
|
|
|
*/
|
|
|
|
|
private class ClassFilter {
|
|
|
|
|
private Class baseClass;
|
|
|
|
|
|
|
|
|
|
public ClassFilter(Class baseClass) {
|
|
|
|
|
this.baseClass = baseClass;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Boolean filter(Class clazz) {
|
2009-03-27 21:27:04 +08:00
|
|
|
return baseClass.isAssignableFrom(clazz) &&
|
|
|
|
|
!Modifier.isAbstract(clazz.getModifiers()) &&
|
|
|
|
|
!Modifier.isInterface(clazz.getModifiers());
|
2009-03-19 01:32:24 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-18 07:22:37 +08:00
|
|
|
/**
|
|
|
|
|
* Instantiate the list of walker classes. Add them to the walker hashmap.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-18 07:22:37 +08:00
|
|
|
* @param walkerClasses Classes to instantiate.
|
2009-03-27 04:45:27 +08:00
|
|
|
* @return map of walker name to walker.
|
2009-03-18 07:22:37 +08:00
|
|
|
*/
|
2009-03-27 21:27:04 +08:00
|
|
|
private Map<String, Class> createWalkerDatabase(List<Class> walkerClasses) {
|
|
|
|
|
Map<String, Class> walkers = new HashMap<String, Class>();
|
2009-03-18 07:22:37 +08:00
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
for (Class<Walker> walkerClass : walkerClasses) {
|
|
|
|
|
String walkerName = getWalkerName(walkerClass);
|
|
|
|
|
logger.info(String.format("* Adding module %s%n", walkerName));
|
|
|
|
|
walkers.put(walkerName, walkerClass);
|
2009-03-18 07:22:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return walkers;
|
|
|
|
|
}
|
2009-03-27 04:45:27 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a name for this type of walker.
|
2009-03-27 21:27:04 +08:00
|
|
|
*
|
2009-03-27 04:45:27 +08:00
|
|
|
* @param walkerType The type of walker.
|
|
|
|
|
* @return A name for this type of walker.
|
|
|
|
|
*/
|
2009-03-27 21:27:04 +08:00
|
|
|
public static String getWalkerName(Class<Walker> walkerType) {
|
2009-03-27 04:45:27 +08:00
|
|
|
String walkerName = "";
|
|
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
if (walkerType.getAnnotation(WalkerName.class) != null)
|
|
|
|
|
walkerName = walkerType.getAnnotation(WalkerName.class).value().trim();
|
2009-03-27 04:45:27 +08:00
|
|
|
|
2009-03-27 21:27:04 +08:00
|
|
|
if (walkerName.length() == 0) {
|
2009-03-27 04:45:27 +08:00
|
|
|
walkerName = walkerType.getSimpleName();
|
2009-03-27 21:27:04 +08:00
|
|
|
if (walkerName.endsWith("Walker"))
|
|
|
|
|
walkerName = walkerName.substring(0, walkerName.lastIndexOf("Walker"));
|
2009-03-27 04:45:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return walkerName;
|
|
|
|
|
}
|
2009-03-18 07:22:37 +08:00
|
|
|
}
|