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 */
019package org.crsh.lang.groovy.command;
020
021import groovy.lang.GroovyRuntimeException;
022import groovy.lang.MissingMethodException;
023import groovy.lang.MissingPropertyException;
024import groovy.lang.Script;
025import org.codehaus.groovy.runtime.InvokerInvocationException;
026import org.crsh.shell.impl.command.spi.CommandCreationException;
027import org.crsh.shell.impl.command.spi.CommandInvoker;
028import org.crsh.command.InvocationContext;
029import org.crsh.command.ScriptException;
030import org.crsh.shell.impl.command.spi.ShellCommand;
031import org.crsh.lang.groovy.closure.PipeLineClosure;
032import org.crsh.lang.groovy.closure.PipeLineInvoker;
033import org.crsh.shell.impl.command.CRaSH;
034import org.crsh.text.RenderPrintWriter;
035
036import java.io.IOException;
037import java.lang.reflect.UndeclaredThrowableException;
038import java.util.LinkedList;
039
040public abstract class GroovyScriptCommand extends Script {
041
042  /** . */
043  private LinkedList<InvocationContext<?>> stack;
044
045  /** The current context. */
046  protected InvocationContext context;
047
048  /** The current output. */
049  protected RenderPrintWriter out;
050
051  protected GroovyScriptCommand() {
052    this.stack = null;
053  }
054
055  public final void pushContext(InvocationContext<?> context) throws NullPointerException {
056    if (context == null) {
057      throw new NullPointerException();
058    }
059
060    //
061    if (stack == null) {
062      stack = new LinkedList<InvocationContext<?>>();
063    }
064
065    // Save current context (is null the first time)
066    stack.addLast((InvocationContext)this.context);
067
068    // Set new context
069    this.context = context;
070    this.out = context.getWriter();
071  }
072
073  public final InvocationContext<?> popContext() {
074    if (stack == null || stack.isEmpty()) {
075      throw new IllegalStateException("Cannot pop a context anymore from the stack");
076    }
077    InvocationContext context = this.context;
078    this.context = stack.removeLast();
079    this.out = this.context != null ? this.context.getWriter() : null;
080    return context;
081  }
082
083  public final void execute(String s) throws ScriptException, IOException {
084    InvocationContext<?> context = peekContext();
085    CommandInvoker invoker = context.resolve(s);
086    invoker.invoke(context);
087  }
088
089  public final InvocationContext<?> peekContext() {
090    return (InvocationContext<?>)context;
091  }
092
093  @Override
094  public final Object invokeMethod(String name, Object args) {
095
096    //
097    try {
098      return super.invokeMethod(name, args);
099    }
100    catch (MissingMethodException missing) {
101      if (context instanceof InvocationContext) {
102        CRaSH crash = (CRaSH)context.getSession().get("crash");
103        if (crash != null) {
104          ShellCommand<?> cmd;
105          try {
106            cmd = crash.getCommand(name);
107          }
108          catch (CommandCreationException ce) {
109            throw new InvokerInvocationException(ce);
110          }
111          if (cmd != null) {
112            InvocationContext<Object> ic = (InvocationContext<Object>)peekContext();
113            PipeLineClosure closure = new PipeLineClosure(ic, name, cmd);
114            PipeLineInvoker evaluation = closure.bind(args);
115            try {
116              evaluation.invoke(ic);
117              return null;
118            }
119            catch (IOException e) {
120              throw new GroovyRuntimeException(e);
121            }
122            catch (UndeclaredThrowableException e) {
123              throw new GroovyRuntimeException(e.getCause());
124            }
125          }
126        }
127      }
128
129      //
130      throw missing;
131    }
132  }
133
134  @Override
135  public final Object getProperty(String property) {
136    if ("out".equals(property)) {
137      if (context instanceof InvocationContext<?>) {
138        return ((InvocationContext<?>)context).getWriter();
139      } else {
140        return null;
141      }
142    } else if ("context".equals(property)) {
143      return context;
144    } else {
145      if (context instanceof InvocationContext<?>) {
146        CRaSH crash = (CRaSH)context.getSession().get("crash");
147        if (crash != null) {
148          try {
149            ShellCommand<?> cmd = crash.getCommand(property);
150            if (cmd != null) {
151              InvocationContext<Object> ic = (InvocationContext<Object>)peekContext();
152              return new PipeLineClosure(ic, property, cmd);
153            }
154          } catch (CommandCreationException e) {
155            throw new InvokerInvocationException(e);
156          }
157        }
158      }
159
160      //
161      try {
162        return super.getProperty(property);
163      }
164      catch (MissingPropertyException e) {
165        return null;
166      }
167    }
168  }
169}