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 groovy.util.BuilderSupport;
024    import org.codehaus.groovy.runtime.InvokerHelper;
025    import org.crsh.text.Color;
026    import org.crsh.text.Renderer;
027    import org.crsh.text.Style;
028    import org.crsh.util.Utils;
029    
030    import java.util.ArrayList;
031    import java.util.Collections;
032    import java.util.Iterator;
033    import java.util.List;
034    import java.util.Map;
035    
036    public class UIBuilder extends BuilderSupport implements Iterable<Renderer> {
037    
038      /** . */
039      private final List<Element> elements;
040    
041      public UIBuilder() {
042        this.elements = new ArrayList<Element>();
043      }
044    
045      public List<Element> getElements() {
046        return elements;
047      }
048    
049      @Override
050      protected Object doInvokeMethod(String methodName, Object name, Object args) {
051        if ("eval".equals(name)) {
052          List list = InvokerHelper.asList(args);
053          if (list.size() == 1 && list.get(0) instanceof Closure) {
054            EvalElement element = (EvalElement)super.doInvokeMethod(methodName, name, null);
055            element.closure = (Closure)list.get(0);
056            return element;
057          } else {
058            return super.doInvokeMethod(methodName, name, args);
059          }
060        } else {
061          return super.doInvokeMethod(methodName, name, args);
062        }
063      }
064    
065      @Override
066      protected Object createNode(Object name) {
067        return createNode(name, (Object)null);
068      }
069    
070      @Override
071      protected Object createNode(Object name, Map attributes, Object value) {
072        Element element;
073        if ("node".equals(name)) {
074          if (value == null) {
075            element = new TreeElement();
076          } else {
077            element = new TreeElement(new LabelElement(value));
078          }
079        } else if ("label".equals(name)) {
080          element = new LabelElement(value);
081        } else if ("table".equals(name)) {
082          TableElement table = new TableElement();
083          table.setHeight((Integer)attributes.get("height"));
084          element = table;
085        } else if ("row".equals(name)) {
086          element = new RowElement();
087        } else if ("header".equals(name)) {
088          element = new RowElement(true);
089        } else if ("eval".equals(name)) {
090          element = new EvalElement();
091        } else {
092          throw new UnsupportedOperationException("Cannot build object with name " + name + " and value " + value);
093        }
094    
095        //
096        Style.Composite style = element.getStyle();
097        if (style == null) {
098          style = Style.style();
099        }
100        style = style.
101            foreground((Color)attributes.get("fg")).
102            foreground((Color)attributes.get("foreground")).
103            background((Color)attributes.get("bg")).
104            background((Color)attributes.get("background")).
105            bold((Boolean)attributes.get("bold")).
106            underline((Boolean)attributes.get("underline")).
107            blink((Boolean)attributes.get("blink"));
108        element.setStyle(style);
109    
110        //
111        if (element instanceof TableElement) {
112          TableElement table = (TableElement)element;
113    
114          // Weights
115          Object weightsAttr = attributes.get("weights");
116          if (weightsAttr instanceof Iterable) {
117            List<Integer> list = Utils.list((Iterable<Integer>)weightsAttr);
118            int[] weights = new int[list.size()];
119            for (int i = 0;i < weights.length;i++) {
120              weights[i] = list.get(i);
121            }
122            table.layout(ColumnLayout.weighted(weights));
123          }
124    
125          // Border
126          Object border = attributes.get("border");
127          Border borderChar;
128          if (border instanceof Boolean && (Boolean)border) {
129            borderChar = Border.dashed;
130          } else if (border instanceof Border) {
131            borderChar = (Border)border;
132          } else {
133            borderChar = null;
134          }
135          table.border(borderChar);
136        }
137    
138        //
139        return element;
140      }
141    
142      @Override
143      protected Object createNode(Object name, Object value) {
144        return createNode(name, Collections.emptyMap(), value);
145      }
146    
147      @Override
148      protected Object createNode(Object name, Map attributes) {
149        return createNode(name, attributes, null);
150      }
151    
152      @Override
153      protected void setParent(Object parent, Object child) {
154        if (parent instanceof TreeElement) {
155          TreeElement parentElement = (TreeElement)parent;
156          Element childElement = (Element)child;
157          parentElement.addChild(childElement);
158        } else if (parent instanceof TableElement) {
159          TableElement parentElement = (TableElement)parent;
160          RowElement childElement = (RowElement)child;
161          parentElement.add(childElement);
162        } else if (parent instanceof RowElement) {
163          RowElement parentElement = (RowElement)parent;
164          Element childElement = (Element)child;
165          if (child instanceof TreeElement) {
166            throw new IllegalArgumentException("A table cannot contain a tree element");
167          }
168          parentElement.add(childElement);
169        } else {
170          throw new UnsupportedOperationException();
171        }
172      }
173    
174      @Override
175      protected void nodeCompleted(Object parent, Object child) {
176        if (parent == null) {
177          elements.add((Element)child);
178        }
179        super.nodeCompleted(parent, child);
180      }
181    
182      public Iterator<Renderer> iterator() {
183        return new Iterator<Renderer>() {
184          Iterator<Element> i = elements.iterator();
185          public boolean hasNext() {
186            return i.hasNext();
187          }
188          public Renderer next() {
189            return i.next().renderer();
190          }
191          public void remove() {
192            throw new UnsupportedOperationException();
193          }
194        };
195      }
196    }