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.LineRenderer;
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<LineRenderer> {
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          element = new TableElement();
083        } else if ("row".equals(name)) {
084          element = new RowElement();
085        } else if ("header".equals(name)) {
086          element = new RowElement(true);
087        } else if ("eval".equals(name)) {
088          element = new EvalElement();
089        } else {
090          throw new UnsupportedOperationException("Cannot build object with name " + name + " and value " + value);
091        }
092    
093        //
094        Style.Composite style = element.getStyle();
095        if (style == null) {
096          style = Style.style();
097        }
098        style = style.
099          bold((Boolean)attributes.get("bold")).
100          underline((Boolean)attributes.get("underline")).
101          blink((Boolean)attributes.get("blink"));
102        if (attributes.containsKey("fg")) {
103          style = style.foreground((Color)attributes.get("fg"));
104        }
105        if (attributes.containsKey("foreground")) {
106          style = style.foreground((Color)attributes.get("foreground"));
107        }
108        if (attributes.containsKey("bg")) {
109          style = style.background((Color)attributes.get("bg"));
110        }
111        if (attributes.containsKey("background")) {
112          style = style.background((Color)attributes.get("background"));
113        }
114        element.setStyle(style);
115    
116        //
117        if (element instanceof TableElement) {
118          TableElement table = (TableElement)element;
119    
120          // Columns
121          Object columns = attributes.get("columns");
122          if (columns instanceof Iterable) {
123            List<Integer> list = Utils.list((Iterable<Integer>)columns);
124            int[] weights = new int[list.size()];
125            for (int i = 0;i < weights.length;i++) {
126              weights[i] = list.get(i);
127            }
128            table.withColumnLayout(Layout.weighted(weights));
129          }
130    
131          // Columns
132          Object rows = attributes.get("rows");
133          if (rows instanceof Iterable) {
134            List<Integer> list = Utils.list((Iterable<Integer>)rows);
135            int[] weights = new int[list.size()];
136            for (int i = 0;i < weights.length;i++) {
137              weights[i] = list.get(i);
138            }
139            table.withRowLayout(Layout.weighted(weights));
140          }
141    
142          // Border
143          Object borderAttr = attributes.get("border");
144          BorderStyle border;
145          if (borderAttr instanceof Boolean && (Boolean)borderAttr) {
146            border = BorderStyle.DASHED;
147          } else if (borderAttr instanceof BorderStyle) {
148            border = (BorderStyle)borderAttr;
149          } else {
150            border = null;
151          }
152          table.border(border);
153    
154          // Separator
155          Object separatorAttr = attributes.get("separator");
156          BorderStyle separator;
157          if (separatorAttr instanceof Boolean && (Boolean)separatorAttr) {
158            separator = BorderStyle.DASHED;
159          } else if (separatorAttr instanceof BorderStyle) {
160            separator = (BorderStyle)separatorAttr;
161          } else {
162            separator = null;
163          }
164          table.separator(separator);
165    
166          // Overflow
167          Object overflowAttr = attributes.get("overflow");
168          Overflow overflow;
169          if ("hidden".equals(overflowAttr)) {
170            overflow = Overflow.HIDDEN;
171          } else if ("wrap".equals(overflowAttr)) {
172            overflow = Overflow.WRAP;
173          } else if (overflowAttr instanceof Overflow) {
174            overflow = (Overflow)separatorAttr;
175          } else {
176            overflow = Overflow.WRAP;
177          }
178          table.overflow(overflow);
179    
180          // Cell left padding
181          Object leftCellPaddingAttr = attributes.get("leftCellPadding");
182          int leftCellPadding = 0;
183          if (leftCellPaddingAttr instanceof Number) {
184            leftCellPadding = ((Number)leftCellPaddingAttr).intValue();
185          }
186          table.setLeftCellPadding(leftCellPadding);
187    
188          // Cell right padding
189          Object rightCellPaddingAttr = attributes.get("rightCellPadding");
190          int rightCellPadding = 0;
191          if (rightCellPaddingAttr instanceof Number) {
192            rightCellPadding = ((Number)rightCellPaddingAttr).intValue();
193          }
194          table.setRightCellPadding(rightCellPadding);
195        }
196    
197        //
198        return element;
199      }
200    
201      @Override
202      protected Object createNode(Object name, Object value) {
203        return createNode(name, Collections.emptyMap(), value);
204      }
205    
206      @Override
207      protected Object createNode(Object name, Map attributes) {
208        return createNode(name, attributes, null);
209      }
210    
211      @Override
212      protected void setParent(Object parent, Object child) {
213        if (parent instanceof TreeElement) {
214          TreeElement parentElement = (TreeElement)parent;
215          Element childElement = (Element)child;
216          parentElement.addChild(childElement);
217        } else if (parent instanceof TableElement) {
218          TableElement parentElement = (TableElement)parent;
219          RowElement childElement = (RowElement)child;
220          parentElement.add(childElement);
221        } else if (parent instanceof RowElement) {
222          RowElement parentElement = (RowElement)parent;
223          Element childElement = (Element)child;
224          if (child instanceof TreeElement) {
225            throw new IllegalArgumentException("A table cannot contain a tree element");
226          }
227          parentElement.add(childElement);
228        } else {
229          throw new UnsupportedOperationException("Unrecognized parent " + parent);
230        }
231      }
232    
233      @Override
234      protected void nodeCompleted(Object parent, Object child) {
235        if (parent == null) {
236          elements.add((Element)child);
237        }
238        super.nodeCompleted(parent, child);
239      }
240    
241      public Iterator<LineRenderer> iterator() {
242        return new Iterator<LineRenderer>() {
243          Iterator<Element> i = elements.iterator();
244          public boolean hasNext() {
245            return i.hasNext();
246          }
247          public LineRenderer next() {
248            return i.next().renderer();
249          }
250          public void remove() {
251            throw new UnsupportedOperationException();
252          }
253        };
254      }
255    }