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    package org.crsh.lang.groovy.command;
020    
021    import groovy.lang.GroovyRuntimeException;
022    import groovy.lang.MissingMethodException;
023    import groovy.lang.MissingPropertyException;
024    import groovy.lang.Script;
025    import org.codehaus.groovy.runtime.InvokerInvocationException;
026    import org.crsh.shell.impl.command.spi.CommandCreationException;
027    import org.crsh.shell.impl.command.spi.CommandInvoker;
028    import org.crsh.command.InvocationContext;
029    import org.crsh.command.ScriptException;
030    import org.crsh.shell.impl.command.spi.ShellCommand;
031    import org.crsh.lang.groovy.closure.PipeLineClosure;
032    import org.crsh.lang.groovy.closure.PipeLineInvoker;
033    import org.crsh.shell.impl.command.CRaSH;
034    import org.crsh.text.RenderPrintWriter;
035    
036    import java.io.IOException;
037    import java.lang.reflect.UndeclaredThrowableException;
038    import java.util.LinkedList;
039    
040    public 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    }