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 }