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.text.ui;
021    
022    import groovy.lang.Closure;
023    import org.crsh.command.CRaSHCommand;
024    import org.crsh.command.CommandInvoker;
025    import org.crsh.lang.groovy.command.GroovyScriptCommand;
026    import org.crsh.command.InvocationContext;
027    import org.crsh.command.ScriptException;
028    import org.crsh.text.Chunk;
029    import org.crsh.text.RenderPrintWriter;
030    import org.crsh.text.Renderable;
031    import org.crsh.text.Renderer;
032    
033    import java.io.IOException;
034    import java.util.LinkedList;
035    import java.util.Map;
036    
037    public class EvalElement extends Element {
038    
039      /** The closure to evaluate. */
040      Closure closure;
041    
042      public Renderer renderer() {
043    
044        Object owner = closure.getOwner();
045    
046        //
047        final InvocationContext ctx;
048        Object cmd;
049        while (true) {
050          if (owner instanceof CRaSHCommand) {
051            cmd = owner;
052            ctx = ((CRaSHCommand)cmd).peekContext();
053            break;
054          } else if (owner instanceof GroovyScriptCommand) {
055            cmd = owner;
056            ctx = ((GroovyScriptCommand)cmd).peekContext();
057            break;
058          } else if (owner instanceof Closure) {
059            owner = ((Closure)owner).getOwner();
060          } else {
061            throw new UnsupportedOperationException("Cannot resolver owner " + owner + " to command");
062          }
063        }
064    
065        //
066        final LinkedList<Renderer> renderers = new LinkedList<Renderer>();
067    
068        //
069        final InvocationContext nested = new InvocationContext() {
070    
071          /** . */
072          private LinkedList<Object> buffer = new LinkedList<Object>();
073    
074          /** . */
075          private Renderable renderable;
076    
077          public CommandInvoker<?, ?> resolve(String s) throws ScriptException, IOException {
078            return ctx.resolve(s);
079          }
080    
081          public boolean isPiped() {
082            throw new UnsupportedOperationException();
083          }
084    
085          public boolean takeAlternateBuffer() {
086            return false;
087          }
088    
089          public boolean releaseAlternateBuffer() {
090            return false;
091          }
092    
093          public RenderPrintWriter getWriter() {
094            return ctx.getWriter();
095          }
096    
097          public Map<String, Object> getSession() {
098            return ctx.getSession();
099          }
100    
101          public Map<String, Object> getAttributes() {
102            return ctx.getAttributes();
103          }
104    
105          public int getWidth() {
106            return ctx.getWidth();
107          }
108    
109          public int getHeight() {
110            return ctx.getHeight();
111          }
112    
113          public String getProperty(String propertyName) {
114            return ctx.getProperty(propertyName);
115          }
116    
117          public String readLine(String msg, boolean echo) {
118            return null;
119          }
120    
121          public Class getConsumedType() {
122            return Object.class;
123          }
124    
125          public void write(Chunk chunk) throws IOException {
126            provide(chunk);
127          }
128    
129          public void provide(Object element) throws IOException {
130            Renderable current = Renderable.getRenderable(element.getClass());
131            if (current == null) {
132              current = Renderable.ANY;
133            }
134            if (current != null) {
135              if (renderable != null && !current.equals(renderable)) {
136                flush();
137              }
138              buffer.addLast(element);
139              renderable = current;
140            }
141          }
142    
143          public void flush() throws IOException {
144            // We don't really flush, we just compute renderables from the buffer
145            if (buffer.size() > 0) {
146              Renderer i = renderable.renderer(buffer.iterator());
147              buffer.clear();
148              renderers.add(i);
149            }
150          }
151    
152          public void close() throws IOException {
153            // Nothing to do, except maybe release resources (and also prevent to do any other operation)
154          }
155        };
156    
157        if (cmd instanceof CRaSHCommand) {
158          ((CRaSHCommand)cmd).pushContext(nested);
159        } else {
160          ((GroovyScriptCommand)cmd).pushContext(nested);
161        }
162        try {
163          closure.call();
164        }
165        finally {
166          if (cmd instanceof CRaSHCommand) {
167            ((CRaSHCommand)cmd).popContext();
168          } else {
169            ((GroovyScriptCommand)cmd).popContext();
170          }
171        }
172    
173        // Be sure to flush
174        try {
175          nested.flush();
176        }
177        catch (IOException e) {
178          e.printStackTrace();
179        }
180    
181        //
182        return Renderer.vertical(renderers);
183      }
184    }