001    /*
002     * Copyright (C) 2012 eXo Platform SAS.
003     *
004     * This is free software; you can redistribute it and/or modify it
005     * under the terms of the GNU Lesser General Public License as
006     * published by the Free Software Foundation; either version 2.1 of
007     * the License, or (at your option) any later version.
008     *
009     * This software is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012     * Lesser General Public License for more details.
013     *
014     * You should have received a copy of the GNU Lesser General Public
015     * License along with this software; if not, write to the Free
016     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018     */
019    
020    package org.crsh.lang.groovy.command;
021    
022    import groovy.lang.Closure;
023    import groovy.lang.GroovyObject;
024    import groovy.lang.MetaClass;
025    import groovy.lang.MissingMethodException;
026    import groovy.lang.MissingPropertyException;
027    import org.codehaus.groovy.runtime.InvokerHelper;
028    import org.codehaus.groovy.runtime.InvokerInvocationException;
029    import org.crsh.cli.impl.descriptor.IntrospectionException;
030    import org.crsh.command.BaseCommand;
031    import org.crsh.command.InvocationContext;
032    import org.crsh.command.NoSuchCommandException;
033    import org.crsh.command.ScriptException;
034    import org.crsh.command.ShellCommand;
035    import org.crsh.shell.impl.command.CRaSH;
036    
037    public abstract class GroovyCommand extends BaseCommand implements GroovyObject {
038    
039      // never persist the MetaClass
040      private transient MetaClass metaClass;
041    
042      protected GroovyCommand() throws IntrospectionException {
043        this.metaClass = InvokerHelper.getMetaClass(this.getClass());
044      }
045    
046      @Override
047      public ScriptException toScript(Throwable cause) {
048        return unwrap(cause);
049      }
050    
051      public static ScriptException unwrap(Throwable cause) {
052        if (cause instanceof ScriptException) {
053          return (ScriptException)cause;
054        } if (cause instanceof groovy.util.ScriptException) {
055          // Special handling for groovy.util.ScriptException
056          // which may be thrown by scripts because it is imported by default
057          // by groovy imports
058          String msg = cause.getMessage();
059          ScriptException translated;
060          if (msg != null) {
061            translated = new ScriptException(msg);
062          } else {
063            translated = new ScriptException();
064          }
065          translated.setStackTrace(cause.getStackTrace());
066          return translated;
067        } else {
068          return new ScriptException(cause);
069        }
070      }
071    
072      public final Object invokeMethod(String name, Object args) {
073        try {
074          return getMetaClass().invokeMethod(this, name, args);
075        }
076        catch (MissingMethodException e) {
077          if (context instanceof InvocationContext) {
078            CRaSH crash = (CRaSH)context.getSession().get("crash");
079            if (crash != null) {
080              ShellCommand cmd;
081              try {
082                cmd = crash.getCommand(name);
083              }
084              catch (NoSuchCommandException ce) {
085                throw new InvokerInvocationException(ce);
086              }
087              if (cmd != null) {
088                // Should we use null instead of "" ?
089                return new ClassDispatcher(cmd, this).dispatch("", CommandClosure.unwrapArgs(args));
090              }
091            }
092          }
093    
094          //
095          Object o = context.getSession().get(name);
096          if (o instanceof Closure) {
097            Closure closure = (Closure)o;
098            if (args instanceof Object[]) {
099              Object[] array = (Object[])args;
100              if (array.length == 0) {
101                return closure.call();
102              } else {
103                return closure.call(array);
104              }
105            } else {
106              return closure.call(args);
107            }
108          } else {
109            throw e;
110          }
111        }
112      }
113    
114      public final Object getProperty(String property) {
115        if (context instanceof InvocationContext<?>) {
116          CRaSH crash = (CRaSH)context.getSession().get("crash");
117          if (crash != null) {
118            try {
119              ShellCommand cmd = crash.getCommand(property);
120              if (cmd != null) {
121                return new ClassDispatcher(cmd, this);
122              }
123            } catch (NoSuchCommandException e) {
124              throw new InvokerInvocationException(e);
125            }
126          }
127        }
128    
129        //
130        try {
131          return getMetaClass().getProperty(this, property);
132        }
133        catch (MissingPropertyException e) {
134          return context.getSession().get(property);
135        }
136      }
137    
138      public final void setProperty(String property, Object newValue) {
139        try {
140          getMetaClass().setProperty(this, property, newValue);
141        }
142        catch (MissingPropertyException e) {
143          context.getSession().put(property, newValue);
144        }
145      }
146    
147      public MetaClass getMetaClass() {
148        if (metaClass == null) {
149          metaClass = InvokerHelper.getMetaClass(getClass());
150        }
151        return metaClass;
152      }
153    
154      public void setMetaClass(MetaClass metaClass) {
155        this.metaClass = metaClass;
156      }
157    }