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 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<Renderer> iterator() {
242 return new Iterator<Renderer>() {
243 Iterator<Element> i = elements.iterator();
244 public boolean hasNext() {
245 return i.hasNext();
246 }
247 public Renderer next() {
248 return i.next().renderer();
249 }
250 public void remove() {
251 throw new UnsupportedOperationException();
252 }
253 };
254 }
255 }