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.command;
021    
022    import groovy.lang.Closure;
023    import groovy.lang.GroovyObjectSupport;
024    import groovy.lang.MissingMethodException;
025    import groovy.lang.MissingPropertyException;
026    import org.codehaus.groovy.runtime.InvokerInvocationException;
027    import org.crsh.shell.impl.command.CRaSH;
028    import org.crsh.text.RenderPrintWriter;
029    
030    import java.util.LinkedList;
031    
032    public abstract class GroovyCommand extends GroovyObjectSupport {
033    
034      /** . */
035      private LinkedList<InvocationContext<?>> stack;
036    
037      /** The current context. */
038      protected CommandContext context;
039    
040      /** The current output. */
041      protected RenderPrintWriter out;
042    
043      protected GroovyCommand() {
044        this.stack = null;
045        this.context = null;
046      }
047    
048      public final void pushContext(InvocationContext<?> context) throws NullPointerException {
049        if (context == null) {
050          throw new NullPointerException();
051        }
052    
053        //
054        if (stack == null) {
055          stack = new LinkedList<InvocationContext<?>>();
056        }
057    
058        // Save current context (is null the first time)
059        stack.addLast((InvocationContext)this.context);
060    
061        // Set new context
062        this.context = context;
063        this.out = context.getWriter();
064      }
065    
066      public final InvocationContext<?> popContext() {
067        if (stack == null || stack.isEmpty()) {
068          throw new IllegalStateException("Cannot pop a context anymore from the stack");
069        }
070        InvocationContext context = (InvocationContext)this.context;
071        this.context = stack.removeLast();
072        this.out = this.context != null ? ((InvocationContext)this.context).getWriter() : null;
073        return context;
074      }
075    
076      public final InvocationContext<?> peekContext() {
077        return (InvocationContext<?>)context;
078      }
079    
080      @Override
081      public final Object invokeMethod(String name, Object args) {
082        try {
083          return super.invokeMethod(name, args);
084        }
085        catch (MissingMethodException e) {
086          if (context instanceof InvocationContext) {
087            CRaSH crash = (CRaSH)context.getSession().get("crash");
088            if (crash != null) {
089              ShellCommand cmd;
090              try {
091                cmd = crash.getCommand(name);
092              }
093              catch (NoSuchCommandException ce) {
094                throw new InvokerInvocationException(ce);
095              }
096              if (cmd != null) {
097                // Should we use null instead of "" ?
098                return new ClassDispatcher(cmd, this).dispatch("", CommandClosure.unwrapArgs(args));
099              }
100            }
101          }
102    
103          //
104          Object o = context.getSession().get(name);
105          if (o instanceof Closure) {
106            Closure closure = (Closure)o;
107            if (args instanceof Object[]) {
108              Object[] array = (Object[])args;
109              if (array.length == 0) {
110                return closure.call();
111              } else {
112                return closure.call(array);
113              }
114            } else {
115              return closure.call(args);
116            }
117          } else {
118            throw e;
119          }
120        }
121      }
122    
123      @Override
124      public final Object getProperty(String property) {
125        if (context instanceof InvocationContext<?>) {
126          CRaSH crash = (CRaSH)context.getSession().get("crash");
127          if (crash != null) {
128            try {
129              ShellCommand cmd = crash.getCommand(property);
130              if (cmd != null) {
131                return new ClassDispatcher(cmd, this);
132              }
133            } catch (NoSuchCommandException e) {
134              throw new InvokerInvocationException(e);
135            }
136          }
137        }
138    
139        //
140        try {
141          return super.getProperty(property);
142        }
143        catch (MissingPropertyException e) {
144          return context.getSession().get(property);
145        }
146      }
147    
148      @Override
149      public final void setProperty(String property, Object newValue) {
150        try {
151          super.setProperty(property, newValue);
152        }
153        catch (MissingPropertyException e) {
154          context.getSession().put(property, newValue);
155        }
156      }
157    }