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
020package org.crsh.text.ui;
021
022import groovy.lang.Closure;
023import groovy.util.BuilderSupport;
024import org.codehaus.groovy.runtime.InvokerHelper;
025import org.crsh.text.Color;
026import org.crsh.text.LineRenderer;
027import org.crsh.text.Style;
028import org.crsh.util.Utils;
029
030import java.util.ArrayList;
031import java.util.Collections;
032import java.util.Iterator;
033import java.util.List;
034import java.util.Map;
035
036public 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}