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
020package org.crsh.lang.groovy.command;
021
022import groovy.lang.Closure;
023import groovy.lang.GroovyObject;
024import groovy.lang.GroovyRuntimeException;
025import groovy.lang.MetaClass;
026import groovy.lang.MissingMethodException;
027import groovy.lang.MissingPropertyException;
028import org.codehaus.groovy.runtime.InvokerHelper;
029import org.codehaus.groovy.runtime.InvokerInvocationException;
030import org.crsh.cli.impl.descriptor.IntrospectionException;
031import org.crsh.command.BaseCommand;
032import org.crsh.shell.impl.command.spi.CommandCreationException;
033import org.crsh.command.InvocationContext;
034import org.crsh.command.ScriptException;
035import org.crsh.shell.impl.command.spi.ShellCommand;
036import org.crsh.lang.groovy.closure.PipeLineClosure;
037import org.crsh.lang.groovy.closure.PipeLineInvoker;
038import org.crsh.shell.impl.command.CRaSH;
039
040import java.io.IOException;
041import java.lang.reflect.UndeclaredThrowableException;
042
043public abstract class GroovyCommand extends BaseCommand implements GroovyObject {
044
045  // never persist the MetaClass
046  private transient MetaClass metaClass;
047
048  protected GroovyCommand() throws IntrospectionException {
049    this.metaClass = InvokerHelper.getMetaClass(this.getClass());
050  }
051
052  @Override
053  public UndeclaredThrowableException toScript(Throwable cause) {
054    if (cause instanceof groovy.util.ScriptException) {
055      cause = unwrap((groovy.util.ScriptException)cause);
056    }
057    return super.toScript(cause);
058  }
059
060  public static ScriptException unwrap(groovy.util.ScriptException cause) {
061    // Special handling for groovy.util.ScriptException
062    // which may be thrown by scripts because it is imported by default
063    // by groovy imports
064    String msg = cause.getMessage();
065    ScriptException translated;
066    if (msg != null) {
067      translated = new ScriptException(msg);
068    } else {
069      translated = new ScriptException();
070    }
071    translated.setStackTrace(cause.getStackTrace());
072    return translated;
073  }
074
075  public static ScriptException unwrap(Throwable cause) {
076    if (cause instanceof ScriptException) {
077      return (ScriptException)cause;
078    } if (cause instanceof groovy.util.ScriptException) {
079      return unwrap((groovy.util.ScriptException)cause);
080    } else {
081      return new ScriptException(cause);
082    }
083  }
084
085  public final Object invokeMethod(String name, Object args) {
086    try {
087      return getMetaClass().invokeMethod(this, name, args);
088    }
089    catch (MissingMethodException missing) {
090      if (context instanceof InvocationContext) {
091        CRaSH crash = (CRaSH)context.getSession().get("crash");
092        if (crash != null) {
093          ShellCommand<?> cmd;
094          try {
095            cmd = crash.getCommand(name);
096          }
097          catch (CommandCreationException ce) {
098            throw new InvokerInvocationException(ce);
099          }
100          if (cmd != null) {
101            InvocationContext<Object> ic = (InvocationContext<Object>)peekContext();
102            PipeLineClosure closure = new PipeLineClosure(ic, name, cmd);
103            PipeLineInvoker evaluation = closure.bind(args);
104            try {
105              evaluation.invoke(ic);
106              return null;
107            }
108            catch (IOException e) {
109              throw new GroovyRuntimeException(e);
110            }
111            catch (UndeclaredThrowableException e) {
112              throw new GroovyRuntimeException(e.getCause());
113            }
114          }
115        }
116      }
117
118      //
119      Object o = context.getSession().get(name);
120      if (o instanceof Closure) {
121        Closure closure = (Closure)o;
122        if (args instanceof Object[]) {
123          Object[] array = (Object[])args;
124          if (array.length == 0) {
125            return closure.call();
126          } else {
127            return closure.call(array);
128          }
129        } else {
130          return closure.call(args);
131        }
132      } else {
133        throw missing;
134      }
135    }
136  }
137
138  public final Object getProperty(String property) {
139    if (context instanceof InvocationContext<?>) {
140      CRaSH crash = (CRaSH)context.getSession().get("crash");
141      if (crash != null) {
142        try {
143          ShellCommand<?> cmd = crash.getCommand(property);
144          if (cmd != null) {
145            InvocationContext<Object> ic = (InvocationContext<Object>)peekContext();
146            return new PipeLineClosure(ic, property, cmd);
147          }
148        } catch (CommandCreationException e) {
149          throw new InvokerInvocationException(e);
150        }
151      }
152    }
153
154    //
155    try {
156      return getMetaClass().getProperty(this, property);
157    }
158    catch (MissingPropertyException e) {
159      return context.getSession().get(property);
160    }
161  }
162
163  public final void setProperty(String property, Object newValue) {
164    try {
165      getMetaClass().setProperty(this, property, newValue);
166    }
167    catch (MissingPropertyException e) {
168      context.getSession().put(property, newValue);
169    }
170  }
171
172  public MetaClass getMetaClass() {
173    if (metaClass == null) {
174      metaClass = InvokerHelper.getMetaClass(getClass());
175    }
176    return metaClass;
177  }
178
179  public void setMetaClass(MetaClass metaClass) {
180    this.metaClass = metaClass;
181  }
182}