/**
* $Id: $
* $Date: $
*
*/
package org.xmlsh.sh.module;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.xmlsh.annotations.AnnotationUtils;
import org.xmlsh.core.AbstractCommand;
import org.xmlsh.core.CoreException;
import org.xmlsh.core.ICommand;
import org.xmlsh.core.IXFunction;
import org.xmlsh.core.ScriptCommand;
import org.xmlsh.core.ScriptCommand.SourceMode;
import org.xmlsh.core.ScriptFunctionCommand;
import org.xmlsh.core.ScriptSource;
import org.xmlsh.core.XClassLoader;
import org.xmlsh.core.XValue;
import org.xmlsh.sh.shell.IFunctionDefiniton;
import org.xmlsh.sh.shell.Shell;
import org.xmlsh.util.FileUtils;
import org.xmlsh.util.JavaUtils;
import org.xmlsh.util.StringPair;
import org.xmlsh.util.Util;
public class PackageModule extends Module {
/*
* Constructor for internal modules like xlmsh
* These dont get their own thread group
*/
protected static Logger mLogger = LogManager.getLogger();
protected PackageModule( ModuleConfig config , XClassLoader loader ) throws CoreException {
super( config , loader );
}
@Override
public String describe() {
return getName() + "[ packages " + Util.join(getPackages(), ",") + " ]";
}
public static String fromReserved(String name) {
if (JavaUtils.isReserved(name))
return "_" + name;
else
return name;
}
/*
* (non-Javadoc)
*
* @see org.xmlsh.sh.shell.IModule#getCommandClass(java.lang.String)
*/
@Override
public ICommand getCommand(String name) throws FileNotFoundException,
URISyntaxException {
/*
* Convert from hyphen-case to camelCase
*/
name = JavaUtils.convertToCamelCase(name);
name = fromReserved(name);
// Store the camel name not the hyphen name
String origName = name;
Class<?> cls = findCommandClass( name );
/*
* First try to find a class that matches name
*/
try {
if( cls == null )
// Cached in AbstractModule
cls = findClass(name, getPackages());
if (cls != null) {
mLogger.trace("Found class matching command: {} " , cls );
if( AnnotationUtils.isCommandClass( cls ) || true ){
Constructor<?> constructor = cls.getConstructor();
if (constructor != null) {
mLogger.trace("Found constructor for class : {} " , constructor );
Object obj = constructor.newInstance();
if (obj instanceof AbstractCommand) {
mLogger.trace("Is instanceof AbstractCommand");
AbstractCommand cmd = (AbstractCommand) obj;
cmd.setModule(this);
return cmd;
} else
if( ! (obj instanceof ICommand) )
getLogger()
.warn("Command class found [ {} ] but is not instance of AbstractCommand.",
cls.getName());
else
return mLogger.exit((ICommand) obj );
}
}
}
} catch (Exception e) {
getLogger().debug("Exception calling constructor for:" + name, e);
}
/*
* Second
* Try a script stored as a resource
*/
// mScriptCache caches a Boolean indicating whether the resource is found or not
// No entry in cache means it has not been tested yet
// Failures are cached with a null command
String scriptName = origName + ".xsh";
URL scriptURL = findResourceInPackages(scriptName, getPackages());
if (scriptURL != null)
return mLogger.exit(new ScriptCommand(new ScriptSource(scriptName, scriptURL,
getTextEncoding() ), SourceMode.RUN, null, this));
return mLogger.exit(null);
}
/*
* (non-Javadoc)
*
* @see org.xmlsh.sh.shell.IModule#getFunctionClass(java.lang.String)
*/
@Override
public IXFunction getFunction(String name) {
String origName = name;
/*
* Try unchanged predeclared functions first
*/
// Try predeclared functions
Class<?> cls = findFunctionClass( name );
try {
/*
* Convert from camelCase to hypen-case
*/
name = JavaUtils.convertToCamelCase(name);
name = fromReserved(name);
if( cls == null )
// Cached in AbstractModule
cls = findClass(name, getPackages());
if (cls != null) {
if( AnnotationUtils.isFunctionClass( cls )){
Constructor<?> constructor = cls.getConstructor();
if (constructor != null) {
Object obj = constructor.newInstance();
if (obj instanceof IXFunction)
return (IXFunction) obj;
if (obj instanceof IFunctionDefiniton) {
IFunctionDefiniton cmd = (IFunctionDefiniton) obj;
return cmd.getFunction();
}
}
}
}
} catch (Exception e) {
mLogger.catching(e);
}
/*
* Try a script
*/
URL scriptURL = findResourceInPackages(origName + ".xsh", getPackages());
if (scriptURL != null)
return new ScriptFunctionCommand(name, scriptURL, this);
return null;
}
protected ModuleConfig getPackageConfig() {
return (ModuleConfig) super.getConfig() ;
}
protected List<String> getPackages() {
return getPackageConfig().getPackages();
}
protected boolean hasCommandResource(String name) {
for (String pkg : getPackages()) {
if (getClassLoader().getResource(toResourceName(name, pkg)) != null)
return true;
}
return false;
}
/*
* (non-Javadoc)
*
* @see org.xmlsh.sh.shell.IModule#hasCommand(java.lang.String)
*/
@Override
public boolean hasHelp(String name) {
try {
// Cached in AbstractModule
Class<?> cls = findClass(name, getPackages());
if (cls != null)
return true;
} catch (Exception e) {
;
}
return hasCommandResource(name + ".xsh");
}
@Override
public URL findResource(String res) {
return findResourceInPackages( res, getPackages() );
}
@Override
public ModuleConfig getModuleConfig(Shell shell , String qname , List<URL> at ) throws Exception {
mLogger.entry(shell, qname, at);
ModuleConfig config = null ;
// If hame has ":" it might be a schemed or prefixed module
StringPair pair = new StringPair(qname, ':');
String name = pair.getRight();
String prefix = pair.getLeft();
IModule mod = null ;
String helpURL = null ; // TODO:
// special scheme
if(prefix != null && Util.isEqual(prefix, "java"))
config = JavaModule.getConfiguration(shell, name, at );
/*
if( mod == null && prefix == null ){
mod = ModuleFactory.createInternalModule( name );
}
*/
if( config == null && prefix == null ){
mLogger.debug("trying to find package module by name: {} " , name );
config = ModuleFactory.getPackageModuleConfig( shell , name , this.getPackages() , null, helpURL );
}
/*
if( mod == nul){
mod = ModuleFactory.createModuleModule(shell, pair , at );
}
*
if( mod == null )
{
// Try to find script source by usual means
ScriptSource script = getScriptSource(shell,qname ,SourceMode.IMPORT , at );
if( script != null )
mod = createScriptModule(shell ,script, qname, at );
}
/*
if( mod == null )
mod = ModuleFactory.createExternalModule(shell, qname, at);
*/
return mLogger.exit(config ) ;
}
private ScriptSource getScriptSource(Shell shell, String name,
List<URL> at) throws URISyntaxException {
String ext = FileUtils.getExt( name );
boolean bIsXsh = ".xsh".equals(ext);
// Failures are cached with a null command
String scriptName = ( bIsXsh || ! Util.isBlank(ext)) ? name : ( name + ".xsh" ) ;
URL scriptURL = findResourceInPackages(scriptName, getPackages());
if (scriptURL != null){
ScriptSource ss = new ScriptSource(scriptName, scriptURL,
getTextEncoding() );
if( ss != null )
return ss ;
}
return null;
}
private ModuleConfig getScriptModuleConfig(Shell shell, ScriptSource script,
String qname, List<URL> at) throws URISyntaxException, IOException, CoreException {
ScriptSource ss = getScriptSource(shell,qname, at );
if( ss !=null )
return ScriptModule.getConfiguration(shell, script ,at);
return null;
}
@Override
public void onInit(Shell shell, List<XValue> args) throws Exception {
}
@Override
public void onLoad(Shell shell) {
super.onLoad(shell);
reflectModuleClass( shell , getClass() );
reflectClassNames( shell , getConfig().getClassNames() );
}
private void reflectClassNames(Shell shell, List<String> classNames) {
mLogger.entry(shell, classNames);
if( classNames == null )
return ;
// Try exact name , module class package relative and package relative
List<String> pkgs = new ArrayList<>();
pkgs.add(null);
pkgs.addAll(getPackages());
for( String className : classNames ){
mLogger.trace("Looking for module referenced class: {}" , className);
Class<?> cls = findClass(className,pkgs);
if( cls != null ){
mLogger.debug("Found for module referenced class: {}" , cls.getName());
reflectClass( shell , cls );
}
}
mLogger.exit();
}
protected void reflectModuleClass(Shell shell, Class<?> cls )
{
reflectClass( shell , cls );
}
private void reflectClass(Shell shell, Class<?> cls) {
mLogger.entry(shell, cls);
List<String> names = AnnotationUtils.getFunctionNames(cls);
// Look for functions
if( ! Util.isEmpty(names) ){
mLogger.debug("found function annotations {}" , names );
cacheFunctionClass( names , cls );
}
names = AnnotationUtils.getCommandNames( cls );
// Look for commands
if( ! Util.isEmpty(names) ){
mLogger.debug("found command annotations {}" , names );
cacheCommandClass( names , cls );
}
for( Class<?> c : cls.getClasses()){
reflectClass(shell,c);
}
mLogger.exit();
}
}
/*
* Copyright (C) 2008-2012 David A. Lee.
*
* The contents of this file are subject to the "Simplified BSD License" (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.opensource.org/licenses/bsd-license.php
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights and limitations under the License.
*
* The Original Code is: all this file.
*
* The Initial Developer of the Original Code is David A. Lee
*
* Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
*
* Contributor(s): David A. Lee
*/