├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── taobao │ │ └── text │ │ ├── CLS.java │ │ ├── Color.java │ │ ├── Decoration.java │ │ ├── Format.java │ │ ├── LineReader.java │ │ ├── LineRenderer.java │ │ ├── RenderAppendable.java │ │ ├── RenderPrintWriter.java │ │ ├── RenderWriter.java │ │ ├── Renderer.java │ │ ├── ScreenBuffer.java │ │ ├── ScreenContext.java │ │ ├── ScreenContextConsumer.java │ │ ├── Screenable.java │ │ ├── Style.java │ │ ├── VirtualScreen.java │ │ ├── lang │ │ ├── HighLightTheme.java │ │ └── LangRenderUtil.java │ │ ├── renderers │ │ ├── BindingRenderer.java │ │ ├── EntityTypeRenderer.java │ │ ├── FileRenderer.java │ │ ├── LogRecordRenderer.java │ │ ├── LoggerRenderer.java │ │ ├── MBeanInfoRenderer.java │ │ ├── MapRenderer.java │ │ ├── MemoryUsageLineRenderer.java │ │ ├── MemoryUsageRenderer.java │ │ ├── ObjectNameRenderer.java │ │ └── ThreadRenderer.java │ │ ├── stream │ │ ├── Consumer.java │ │ ├── Filter.java │ │ └── Producer.java │ │ ├── ui │ │ ├── BorderStyle.java │ │ ├── Element.java │ │ ├── ElementRenderer.java │ │ ├── LabelElement.java │ │ ├── LabelLineRenderer.java │ │ ├── LabelReader.java │ │ ├── Layout.java │ │ ├── Overflow.java │ │ ├── RowElement.java │ │ ├── RowLineRenderer.java │ │ ├── TableElement.java │ │ ├── TableLineRenderer.java │ │ ├── TableRowLineRenderer.java │ │ ├── TableRowReader.java │ │ ├── TextElement.java │ │ ├── TreeElement.java │ │ ├── TreeLineRenderer.java │ │ ├── UIBuilder.java │ │ └── UIBuilderRenderer.java │ │ └── util │ │ ├── BaseIterator.java │ │ ├── BlankSequence.java │ │ ├── CharSlicer.java │ │ ├── Pair.java │ │ ├── RenderUtil.java │ │ └── Utils.java └── resources │ └── com │ └── taobao │ └── text │ └── ui │ └── themes │ └── default.xml └── test └── java └── com └── taobao └── text └── ui └── example ├── CharSlicerStackOverflow.java ├── HighlightExample.java ├── MapExample.java ├── POJOExample.java └── TableExample.java /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | *.sw? 3 | .#* 4 | *# 5 | *~ 6 | /build 7 | /code 8 | .classpath 9 | .project 10 | .settings 11 | .metadata 12 | .factorypath 13 | .recommenders 14 | bin 15 | build 16 | lib/ 17 | target 18 | .factorypath 19 | .springBeans 20 | interpolated*.xml 21 | dependency-reduced-pom.xml 22 | build.log 23 | _site/ 24 | .*.md.html 25 | manifest.yml 26 | MANIFEST.MF 27 | settings.xml 28 | activemq-data 29 | overridedb.* 30 | *.iml 31 | *.ipr 32 | *.iws 33 | .idea 34 | *.jar 35 | .DS_Store 36 | .factorypath 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | text-ui 3 | === 4 | 5 | ## Introduction 6 | 7 | This code extract from crash shell: https://github.com/crashub/crash/tree/1.3.2/shell 8 | 9 | 10 | ### 输出Table的例子(自适应列宽,字体颜色等) 11 | 12 | 可以用maven查看运行效果: 13 | 14 | ```bash 15 | mvn test -Dtest=com.taobao.text.ui.example.TableExample 16 | ``` 17 | 18 | ```java 19 | import static com.taobao.text.ui.Element.label; 20 | import static com.taobao.text.ui.Element.row; 21 | 22 | import org.junit.Test; 23 | 24 | import com.taobao.text.ui.BorderStyle; 25 | import com.taobao.text.ui.Overflow; 26 | import com.taobao.text.ui.TableElement; 27 | import com.taobao.text.util.RenderUtil; 28 | 29 | 30 | public class TableExample { 31 | 32 | @Test 33 | public void test1() { 34 | // header定义 35 | String[] fields = { "name", "age" }; 36 | 37 | // 设置两列的比例是1:1,如果不设置的话,列宽是自动按元素最长的处理。 38 | // 设置table的外部边框,默认是没有外边框 39 | // 还有内部的分隔线,默认内部没有分隔线 40 | TableElement tableElement = new TableElement(1, 1).border(BorderStyle.DASHED).separator(BorderStyle.DASHED); 41 | 42 | // 设置单元格的左右边框间隔,默认是没有,看起来会有点挤,空间足够时,可以设置为1,看起来清爽 43 | tableElement.leftCellPadding(1).rightCellPadding(1); 44 | 45 | // 设置header 46 | tableElement.row(true, fields); 47 | 48 | // 设置cell里的元素超出了处理方式,Overflow.HIDDEN 表示隐藏 49 | // Overflow.WRAP表示会向外面排出去,即当输出宽度有限时,右边的列可能会显示不出,被挤掉了 50 | tableElement.overflow(Overflow.HIDDEN); 51 | 52 | // 设置第一列输出字体蓝色,红色背景 53 | // 设置第二列字体加粗,加下划线 54 | for (int i = 0; i < 10; ++i) { 55 | tableElement.add(row().add(label("student" + i).style(Composite.style(Color.blue).bg(Color.red))) 56 | .add(label("" + i).style(Decoration.bold.underline()))); 57 | } 58 | 59 | // 默认输出宽度是80 60 | System.err.println(RenderUtil.render(tableElement)); 61 | } 62 | } 63 | ``` 64 | 65 | ### 输出POJO List的例子 66 | 67 | ```java 68 | public class POJOExample { 69 | 70 | public class Student { 71 | String name; 72 | int age; 73 | 74 | public Student(String name, int age) { 75 | this.name = name; 76 | this.age = age; 77 | } 78 | 79 | public String getName() { 80 | return name; 81 | } 82 | 83 | public void setName(String name) { 84 | this.name = name; 85 | } 86 | 87 | public int getAge() { 88 | return age; 89 | } 90 | 91 | public void setAge(int age) { 92 | this.age = age; 93 | } 94 | } 95 | 96 | @Test 97 | public void test1(){ 98 | 99 | List list = new ArrayList(); 100 | 101 | for(int i = 0; i < 10; ++i){ 102 | list.add(new Student("name" + i, 10 + i)); 103 | } 104 | 105 | System.err.println(RenderUtil.render(list)); 106 | } 107 | } 108 | ``` 109 | 110 | ## 设置Element的Style 111 | 112 | Style有下面六种 113 | 114 | ``` 115 | Boolean bold 是否粗体 116 | Boolean underline 是否有下划线 117 | Boolean blink 是否闪烁 118 | Color foreground 前景颜色 119 | Color background 背景颜色 120 | ``` 121 | 122 | 对于每一个Element都可以设置Style,比如一个Table里的一个小格子。 123 | 124 | 设置Style可以通过Composite来构造,比如 125 | 126 | ```java 127 | //设置字体颜色为蓝色 128 | Element.label("xxx").style(Composite.style(Color.blue)); 129 | ``` 130 | 131 | 也可以通过Decoration这个辅助类来设置,这个类定义了一些常见的Style。 132 | 133 | ```java 134 | //设置为粗体,背景颜色是红色 135 | Element.label("xxx").style(Decoration.bold.bg(Color.red)); 136 | ``` 137 | 138 | ## 语法高亮功能 139 | 支持对多种语法的语法高亮。默认是java语言,可以传递不同的语言参数。 140 | 141 | ```java 142 | public class HighlightExample { 143 | 144 | public static void main(String[] args) { 145 | 146 | String code = "int a = 123; \nString s = \"sssss\";"; 147 | 148 | System.out.println(LangRenderUtil.render(code, LangRenderUtil.java)); 149 | } 150 | 151 | } 152 | ``` 153 | 154 | 需要增加依赖 155 | 156 | ```xml 157 | 158 | com.fifesoft 159 | rsyntaxtextarea 160 | 2.5.8 161 | 162 | ``` 163 | 164 | 165 | 更多的例子请参考test下面的example。 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | org.sonatype.oss 6 | oss-parent 7 | 9 8 | 9 | 10 | text-ui 11 | https://github.com/alibaba/text-ui 12 | 2018 13 | 14 | 15 | 16 | Apache 2 17 | http://www.apache.org/licenses/LICENSE-2.0.txt 18 | repo 19 | A business-friendly OSS license 20 | 21 | 22 | 23 | 24 | 25 | hengyunabc 26 | hengyunabc 27 | hengyunabc@gmail.com 28 | 29 | 30 | 31 | 32 | scm:git:git@github.com:alibaba/text-ui.git 33 | scm:git:git@github.com:alibaba/text-ui.git 34 | https://github.com/alibaba/text-ui 35 | 36 | 37 | com.taobao.text 38 | text-ui 39 | 0.0.4-SNAPSHOT 40 | jar 41 | 42 | text.ui 43 | 44 | 45 | 1.6 46 | 1.6 47 | 1.6 48 | UTF-8 49 | UTF-8 50 | 51 | 52 | 1.2.3 53 | 1.7.5 54 | 55 | 56 | 4.13.1 57 | 58 | 59 | 60 | 61 | 62 | org.codehaus.groovy 63 | groovy-all 64 | 1.8.9 65 | provided 66 | 67 | 68 | 69 | com.fifesoft 70 | rsyntaxtextarea 71 | 2.5.8 72 | provided 73 | 74 | 75 | 76 | org.slf4j 77 | slf4j-api 78 | ${slf4j.version} 79 | 80 | 81 | 82 | org.slf4j 83 | jcl-over-slf4j 84 | ${slf4j.version} 85 | test 86 | 87 | 88 | org.slf4j 89 | log4j-over-slf4j 90 | ${slf4j.version} 91 | test 92 | 93 | 94 | ch.qos.logback 95 | logback-core 96 | ${logback.version} 97 | test 98 | 99 | 100 | ch.qos.logback 101 | logback-classic 102 | ${logback.version} 103 | test 104 | 105 | 106 | 107 | 108 | junit 109 | junit 110 | ${junit.version} 111 | test 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | org.apache.maven.plugins 120 | maven-compiler-plugin 121 | 3.5.1 122 | 123 | 1.6 124 | 1.6 125 | 1.6 126 | 1.6 127 | 128 | 129 | 130 | org.apache.maven.plugins 131 | maven-jar-plugin 132 | 3.0.1 133 | 134 | 135 | default-jar 136 | 137 | 138 | /examples/** 139 | 140 | 141 | 142 | 143 | 144 | 145 | org.apache.maven.plugins 146 | maven-javadoc-plugin 147 | 3.0.1 148 | 149 | 150 | -Xdoclint:none 151 | 152 | 153 | 154 | attach-javadocs 155 | 156 | jar 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/CLS.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | /** 23 | * Clears the screen. 24 | */ 25 | public class CLS { 26 | 27 | /** . */ 28 | public static final CLS INSTANCE = new CLS(); 29 | 30 | private CLS() { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/Color.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | /** 23 | * A color representing the available ansi colors as well as an additional color {@link #def} that symbolize the 24 | * default color. 25 | */ 26 | public enum Color { 27 | 28 | /** . */ 29 | black(0), 30 | 31 | /** . */ 32 | red(1), 33 | 34 | /** . */ 35 | green(2), 36 | 37 | /** . */ 38 | yellow(3), 39 | 40 | /** . */ 41 | blue(4), 42 | 43 | /** . */ 44 | magenta(5), 45 | 46 | /** . */ 47 | cyan(6), 48 | 49 | /** . */ 50 | white(7), 51 | 52 | /** . */ 53 | def(9); 54 | 55 | /** . */ 56 | public final int code; 57 | 58 | public final char c; 59 | 60 | private Color(int code) { 61 | this.code = code; 62 | this.c = Character.forDigit(code, 10); 63 | } 64 | 65 | public Style.Composite fg() { 66 | return Style.style(null, this, null); 67 | } 68 | 69 | public Style.Composite foreground() { 70 | return Style.style(null, this, null); 71 | } 72 | 73 | public Style.Composite bg() { 74 | return Style.style(null, null, this); 75 | } 76 | 77 | public Style.Composite background() { 78 | return Style.style(null, null, this); 79 | } 80 | 81 | public Style.Composite bold() { 82 | return bold(true); 83 | } 84 | 85 | public Style.Composite underline() { 86 | return underline(true); 87 | } 88 | 89 | public Style.Composite blink() { 90 | return blink(true); 91 | } 92 | 93 | public Style.Composite bold(Boolean value) { 94 | return Style.style(this).bold(value); 95 | } 96 | 97 | public Style.Composite underline(Boolean value) { 98 | return Style.style(this).underline(value); 99 | } 100 | 101 | public Style.Composite blink(Boolean value) { 102 | return Style.style(this).blink(value); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/Decoration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | public enum Decoration { 23 | 24 | /** . */ 25 | bold(0, true, "1"), 26 | 27 | /** . */ 28 | bold_off(0, false, "22"), 29 | 30 | /** . */ 31 | underline(1, true, "4"), 32 | 33 | /** . */ 34 | underline_off(1, false, "24"), 35 | 36 | /** . */ 37 | blink(2, true, "5"), 38 | 39 | /** . */ 40 | blink_off(2, false, "25"); 41 | 42 | /** . */ 43 | final int index; 44 | 45 | /** . */ 46 | final boolean on; 47 | 48 | /** . */ 49 | public final String code; 50 | 51 | private Decoration(int index, boolean on, String code) { 52 | this.index = index; 53 | this.on = on; 54 | this.code = code; 55 | } 56 | 57 | public Style.Composite fg(Color value) { 58 | return foreground(value); 59 | } 60 | 61 | public Style.Composite foreground(Color value) { 62 | return Style.style(this).foreground(value); 63 | } 64 | 65 | public Style.Composite bg(Color value) { 66 | return background(value); 67 | } 68 | 69 | public Style.Composite background(Color value) { 70 | return Style.style(this).background(value); 71 | } 72 | 73 | public Style.Composite bold() { 74 | return bold(true); 75 | } 76 | 77 | public Style.Composite underline() { 78 | return underline(true); 79 | } 80 | 81 | public Style.Composite blink() { 82 | return blink(true); 83 | } 84 | 85 | public Style.Composite bold(Boolean value) { 86 | return Style.style(this).bold(value); 87 | } 88 | 89 | public Style.Composite underline(Boolean value) { 90 | return Style.style(this).underline(value); 91 | } 92 | 93 | public Style.Composite blink(Boolean value) { 94 | return Style.style(this).blink(value); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/Format.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | package com.taobao.text; 20 | 21 | import java.io.IOException; 22 | 23 | /** @author Julien Viet */ 24 | public abstract class Format { 25 | 26 | /** . */ 27 | public static final Text TEXT = new Text(); 28 | 29 | /** . */ 30 | public static final Ansi ANSI = new Ansi(); 31 | 32 | /** . */ 33 | public static final PreHtml PRE_HTML = new PreHtml(); 34 | 35 | public static class Text extends Format { 36 | protected Text() { 37 | } 38 | @Override 39 | public void begin(Appendable to) throws IOException { 40 | } 41 | @Override 42 | public void write(CharSequence s, Appendable to) throws IOException { 43 | if (s.length() > 0) { 44 | to.append(s); 45 | } 46 | } 47 | 48 | @Override 49 | public void write(char c, Appendable to) throws IOException { 50 | to.append(c); 51 | } 52 | 53 | @Override 54 | public void write(Style style, Appendable to) throws IOException { 55 | } 56 | @Override 57 | public void cls(Appendable to) throws IOException { 58 | } 59 | @Override 60 | public void end(Appendable to) throws IOException { 61 | } 62 | } 63 | 64 | public static class Ansi extends Format { 65 | protected Ansi() { 66 | } 67 | @Override 68 | public void begin(Appendable to) throws IOException { 69 | } 70 | @Override 71 | public void write(CharSequence s, Appendable to) throws IOException { 72 | if (s.length() > 0) { 73 | to.append(s); 74 | } 75 | } 76 | @Override 77 | public void write(char c, Appendable to) throws IOException { 78 | to.append(c); 79 | } 80 | @Override 81 | public void write(Style style, Appendable to) throws IOException { 82 | style.writeAnsiTo(to); 83 | } 84 | @Override 85 | public void cls(Appendable to) throws IOException { 86 | } 87 | @Override 88 | public void end(Appendable to) throws IOException { 89 | } 90 | } 91 | 92 | public static class PreHtml extends Format { 93 | protected PreHtml() { 94 | } 95 | @Override 96 | public void begin(Appendable to) throws IOException { 97 | to.append("
");
 98 |     }
 99 |     @Override
100 |     public void write(CharSequence s, Appendable to) throws IOException {
101 |       if (s.length() > 0) {
102 |         for (int i = 0;i < s.length();i++) {
103 |           this.write(s.charAt(i), to);
104 |         }
105 |       }
106 |     }
107 |     @Override
108 |     public void write(char c, Appendable to) throws IOException {
109 |       switch (c) {
110 |         case '>':
111 |           to.append(">");
112 |           break;
113 |         case '<':
114 |           to.append("<");
115 |           break;
116 |         case '&':
117 |           to.append("&");
118 |           break;
119 |         case '\'':
120 |           to.append("'");
121 |           break;
122 |         case '"':
123 |           to.append(""");
124 |           break;
125 |         default:
126 |           to.append(c);
127 |       }
128 |     }
129 |     @Override
130 |     public void write(Style style, Appendable to) throws IOException {
131 |     }
132 |     @Override
133 |     public void cls(Appendable to) throws IOException {
134 |     }
135 |     @Override
136 |     public void end(Appendable to) throws IOException {
137 |       to.append("
"); 138 | } 139 | } 140 | 141 | public abstract void begin(Appendable to) throws IOException; 142 | 143 | public abstract void write(CharSequence s, Appendable to) throws IOException; 144 | 145 | public abstract void write(char charValue, Appendable to) throws IOException; 146 | 147 | public abstract void write(Style style, Appendable to) throws IOException; 148 | 149 | public abstract void cls(Appendable to) throws IOException; 150 | 151 | public abstract void end(Appendable to) throws IOException; 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/LineReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | /** 23 | * The line reader. 24 | */ 25 | public interface LineReader { 26 | 27 | /** 28 | * Returns true if the renderer has a next line to render. 29 | * 30 | * @return when there is at least a next line to read 31 | */ 32 | boolean hasLine(); 33 | 34 | /** 35 | * Renders the element. 36 | * 37 | * @param to the buffer for rendering 38 | * @throws IllegalStateException when there is no line to render 39 | */ 40 | void renderLine(RenderAppendable to) throws IllegalStateException; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/LineRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | import java.util.Iterator; 23 | 24 | /** 25 | * A line oriented renderer. 26 | */ 27 | public abstract class LineRenderer { 28 | 29 | public static final LineRenderer NULL = new LineRenderer() { 30 | @Override 31 | public int getActualWidth() { 32 | return 0; 33 | } 34 | @Override 35 | public int getMinWidth() { 36 | return 0; 37 | } 38 | @Override 39 | public int getMinHeight(int width) { 40 | return 0; 41 | } 42 | @Override 43 | public int getActualHeight(int width) { 44 | return 0; 45 | } 46 | @Override 47 | public LineReader reader(int width) { 48 | return new LineReader() { 49 | public boolean hasLine() { 50 | return false; 51 | } 52 | public void renderLine(RenderAppendable to) throws IllegalStateException { 53 | throw new IllegalStateException(); 54 | } 55 | }; 56 | } 57 | }; 58 | 59 | public static LineRenderer vertical(Iterable renderers) { 60 | Iterator i = renderers.iterator(); 61 | if (i.hasNext()) { 62 | LineRenderer renderer = i.next(); 63 | if (i.hasNext()) { 64 | return new Composite(renderers); 65 | } else { 66 | return renderer; 67 | } 68 | } else { 69 | return NULL; 70 | } 71 | } 72 | 73 | /** 74 | * Returns the element actual width. 75 | * 76 | * @return the actual width 77 | */ 78 | public abstract int getActualWidth(); 79 | 80 | /** 81 | * Returns the element minimum width. 82 | * 83 | * @return the minimum width 84 | */ 85 | public abstract int getMinWidth(); 86 | 87 | /** 88 | * Return the minimum height for the specified with. 89 | * 90 | * @param width the width 91 | * @return the actual height 92 | */ 93 | public abstract int getMinHeight(int width); 94 | 95 | /** 96 | * Return the actual height for the specified with. 97 | * 98 | * @param width the width 99 | * @return the minimum height 100 | */ 101 | public abstract int getActualHeight(int width); 102 | 103 | /** 104 | * Create a renderer for the specified width and height or return null if the element does not provide any output 105 | * for the specified dimensions. The default implementation delegates to the {@link #reader(int)} method when the 106 | * height argument is not positive otherwise it returns null. Subclasses should override this method 107 | * when they want to provide content that can adapts to the specified height. 108 | * 109 | * @param width the width 110 | * @param height the height 111 | * @return the renderer 112 | */ 113 | public LineReader reader(int width, int height) { 114 | if (height > 0) { 115 | return null; 116 | } else { 117 | return reader(width); 118 | } 119 | } 120 | 121 | /** 122 | * Create a renderer for the specified width or return null if the element does not provide any output. 123 | * 124 | * @param width the width 125 | * @return the renderer 126 | */ 127 | public abstract LineReader reader(int width); 128 | 129 | /** 130 | * Renders this object to the provided output. 131 | * 132 | * @param out the output 133 | */ 134 | public final void render(RenderAppendable out) { 135 | LineReader renderer = reader(out.getWidth()); 136 | if (renderer != null) { 137 | while (renderer.hasLine()) { 138 | renderer.renderLine(out); 139 | out.append('\n'); 140 | } 141 | } 142 | } 143 | 144 | private static class Composite extends LineRenderer { 145 | 146 | /** . */ 147 | private final Iterable renderers; 148 | 149 | /** . */ 150 | private final int actualWidth; 151 | 152 | /** . */ 153 | private final int minWidth; 154 | 155 | private Composite(Iterable renderers) { 156 | 157 | int actualWidth = 0; 158 | int minWidth = 0; 159 | for (LineRenderer renderer : renderers) { 160 | actualWidth = Math.max(actualWidth, renderer.getActualWidth()); 161 | minWidth = Math.max(minWidth, renderer.getMinWidth()); 162 | } 163 | 164 | this.actualWidth = actualWidth; 165 | this.minWidth = minWidth; 166 | this.renderers = renderers; 167 | } 168 | 169 | @Override 170 | public int getActualWidth() { 171 | return actualWidth; 172 | } 173 | 174 | @Override 175 | public int getMinWidth() { 176 | return minWidth; 177 | } 178 | 179 | @Override 180 | public int getActualHeight(int width) { 181 | int actualHeight = 0; 182 | for (LineRenderer renderer : renderers) { 183 | actualHeight += renderer.getActualHeight(width); 184 | } 185 | return actualHeight; 186 | } 187 | 188 | @Override 189 | public int getMinHeight(int width) { 190 | return 1; 191 | } 192 | 193 | @Override 194 | public LineReader reader(final int width, final int height) { 195 | 196 | final Iterator i = renderers.iterator(); 197 | 198 | // 199 | return new LineReader() { 200 | 201 | /** . */ 202 | private LineReader current; 203 | 204 | /** . */ 205 | private int index = 0; 206 | 207 | public boolean hasLine() { 208 | if (height > 0 && index >= height) { 209 | return false; 210 | } else { 211 | if (current == null || !current.hasLine()) { 212 | while (i.hasNext()) { 213 | LineRenderer next = i.next(); 214 | LineReader reader = next.reader(width); 215 | if (reader != null && reader.hasLine()) { 216 | current = reader; 217 | return true; 218 | } 219 | } 220 | return false; 221 | } else { 222 | return true; 223 | } 224 | } 225 | } 226 | 227 | public void renderLine(RenderAppendable to) throws IllegalStateException { 228 | if (hasLine()) { 229 | current.renderLine(to); 230 | index++; 231 | } else { 232 | throw new IllegalStateException(); 233 | } 234 | } 235 | }; 236 | } 237 | 238 | @Override 239 | public LineReader reader(final int width) { 240 | return reader(width, -1); 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/RenderAppendable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | import java.io.IOException; 23 | import java.util.LinkedList; 24 | 25 | public class RenderAppendable implements ScreenContext { 26 | 27 | /** . */ 28 | private final ScreenContext context; 29 | 30 | /** . */ 31 | private LinkedList stack; 32 | 33 | public RenderAppendable(ScreenContext context) { 34 | this.context = context; 35 | } 36 | 37 | @Override 38 | public RenderAppendable append(CharSequence s) { 39 | try { 40 | context.append(s); 41 | } 42 | catch (java.io.IOException ignore) { 43 | } 44 | return this; 45 | } 46 | 47 | @Override 48 | public Screenable append(char c) { 49 | try { 50 | context.append(c); 51 | } 52 | catch (java.io.IOException ignore) { 53 | } 54 | return this; 55 | } 56 | 57 | @Override 58 | public Screenable append(CharSequence csq, int start, int end) { 59 | try { 60 | context.append(csq, start, end); 61 | } 62 | catch (java.io.IOException ignore) { 63 | } 64 | return this; 65 | } 66 | 67 | @Override 68 | public Screenable append(Style style) { 69 | try { 70 | context.append(style); 71 | } 72 | catch (java.io.IOException ignore) { 73 | } 74 | return this; 75 | } 76 | 77 | @Override 78 | public Screenable cls() { 79 | try { 80 | context.cls(); 81 | } 82 | catch (java.io.IOException ignore) { 83 | } 84 | return this; 85 | } 86 | 87 | public int getWidth() { 88 | // Use one less char to have a correct display on windows 89 | return Math.max(0, context.getWidth() - 1); 90 | } 91 | 92 | public int getHeight() { 93 | return context.getHeight(); 94 | } 95 | 96 | public void flush() throws IOException { 97 | context.flush(); 98 | } 99 | 100 | public void enterStyle(Style.Composite style) { 101 | if (stack == null) { 102 | stack = new LinkedList(); 103 | } 104 | append(style); 105 | stack.addLast(style); 106 | } 107 | 108 | public Style.Composite leaveStyle() { 109 | if (stack == null || stack.isEmpty()) { 110 | throw new IllegalStateException("Cannot leave non existing style"); 111 | } 112 | Style.Composite last = stack.removeLast(); 113 | if (stack.size() > 0) { 114 | 115 | // Compute merged 116 | Style.Composite merged = getMerged(); 117 | 118 | // Compute diff with removed 119 | Boolean bold = foo(last.getBold(), merged.getBold()); 120 | Boolean underline = foo(last.getUnderline(), merged.getUnderline()); 121 | Boolean blink = foo(last.getBlink(), merged.getBlink()); 122 | 123 | // For now we assume that black is the default background color 124 | // and white is the default foreground color 125 | Color fg = foo(last.getForeground(), merged.getForeground(), Color.def); 126 | Color bg = foo(last.getBackground(), merged.getBackground(), Color.def); 127 | 128 | // 129 | Style.Composite bilto = Style.style(bold, underline, blink, fg, bg); 130 | 131 | // 132 | append(bilto); 133 | } else { 134 | append(Style.reset); 135 | } 136 | return last; 137 | } 138 | 139 | /** 140 | * Compute the current merged style. 141 | * 142 | * @return the merged style 143 | */ 144 | private Style.Composite getMerged() { 145 | Style.Composite merged = Style.style(); 146 | for (Style s : stack) { 147 | merged = (Style.Composite)merged.merge(s); 148 | } 149 | return merged; 150 | } 151 | 152 | private Boolean foo(Boolean last, Boolean merged) { 153 | if (last != null) { 154 | if (merged != null) { 155 | return merged; 156 | } else { 157 | return !last; 158 | } 159 | } else { 160 | return null; 161 | } 162 | } 163 | 164 | private Color foo(Color last, Color merged, Color def) { 165 | if (last != null) { 166 | if (merged != null) { 167 | return merged; 168 | } else { 169 | return def; 170 | } 171 | } else { 172 | return null; 173 | } 174 | } 175 | 176 | public void styleOff() { 177 | if (stack != null && stack.size() > 0) { 178 | append(Style.reset); 179 | } 180 | } 181 | 182 | public void styleOn() { 183 | if (stack != null && stack.size() > 0) { 184 | append(getMerged()); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/RenderPrintWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | import java.io.Closeable; 23 | import java.io.IOException; 24 | import java.io.InterruptedIOException; 25 | import java.io.PrintWriter; 26 | 27 | import com.taobao.text.ui.Element; 28 | 29 | public class RenderPrintWriter extends PrintWriter { 30 | 31 | /** . */ 32 | private final RenderWriter out; 33 | 34 | public RenderPrintWriter(ScreenContext out) { 35 | super(new RenderWriter(out)); 36 | 37 | // 38 | this.out = (RenderWriter)super.out; 39 | } 40 | 41 | public RenderPrintWriter(ScreenContext out, Closeable closeable) { 42 | super(new RenderWriter(out, closeable)); 43 | 44 | // 45 | this.out = (RenderWriter)super.out; 46 | } 47 | 48 | public final boolean isEmpty() { 49 | return out.isEmpty(); 50 | } 51 | 52 | public final void print(Object obj, Color foreground) { 53 | try { 54 | out.append(Style.style(foreground)); 55 | } 56 | catch (InterruptedIOException x) { 57 | Thread.currentThread().interrupt(); 58 | } 59 | catch (IOException x) { 60 | setError(); 61 | } 62 | print(obj); 63 | try { 64 | out.append(Style.reset); 65 | } 66 | catch (InterruptedIOException x) { 67 | Thread.currentThread().interrupt(); 68 | } 69 | catch (IOException x) { 70 | setError(); 71 | } 72 | } 73 | 74 | public final void println(Object obj, Color foreground) { 75 | print(obj, Style.style(foreground)); 76 | println(); 77 | } 78 | 79 | public final void print(Object obj, Color foreground, Color background) { 80 | try { 81 | out.append(Style.style(foreground, background)); 82 | } 83 | catch (InterruptedIOException x) { 84 | Thread.currentThread().interrupt(); 85 | } 86 | catch (IOException x) { 87 | setError(); 88 | } 89 | print(obj); 90 | try { 91 | out.append(Style.reset); 92 | } 93 | catch (InterruptedIOException x) { 94 | Thread.currentThread().interrupt(); 95 | } 96 | catch (IOException x) { 97 | setError(); 98 | } 99 | } 100 | 101 | public final void println(Object obj, Color foreground, Color background) { 102 | print(obj, Style.style(foreground, background)); 103 | println(); 104 | } 105 | 106 | public final void print(Object obj, Decoration decoration) { 107 | try { 108 | out.append(Style.style(decoration)); 109 | } 110 | catch (InterruptedIOException x) { 111 | Thread.currentThread().interrupt(); 112 | } 113 | catch (IOException x) { 114 | setError(); 115 | } 116 | print(obj); 117 | try { 118 | out.append(Style.reset); 119 | } 120 | catch (InterruptedIOException x) { 121 | Thread.currentThread().interrupt(); 122 | } 123 | catch (IOException x) { 124 | setError(); 125 | } 126 | } 127 | 128 | public final void println(Object obj, Decoration decoration) { 129 | print(obj, Style.style(decoration)); 130 | println(); 131 | } 132 | 133 | public final void print(Object obj, Decoration decoration, Color foreground) { 134 | print(obj, Style.style(decoration, foreground)); 135 | println(); 136 | } 137 | 138 | public final void println(Object obj, Decoration decoration, Color foreground) { 139 | print(obj, Style.style(decoration, foreground, null)); 140 | println(); 141 | } 142 | 143 | public final void print(Object obj, Decoration decoration, Color foreground, Color background) { 144 | print(obj, Style.style(decoration, foreground, background)); 145 | println(); 146 | } 147 | 148 | public final void println(Object obj, Decoration decoration, Color foreground, Color background) { 149 | print(obj, Style.style(decoration, foreground, background)); 150 | println(); 151 | } 152 | 153 | public final void print(Object obj, Style style) { 154 | try { 155 | out.append(style); 156 | } 157 | catch (InterruptedIOException x) { 158 | Thread.currentThread().interrupt(); 159 | } 160 | catch (IOException x) { 161 | setError(); 162 | } 163 | print(obj); 164 | try { 165 | out.append(Style.reset); 166 | } 167 | catch (InterruptedIOException x) { 168 | Thread.currentThread().interrupt(); 169 | } 170 | catch (IOException x) { 171 | setError(); 172 | } 173 | } 174 | 175 | public final void println(Object obj, Style style) { 176 | print(obj, style); 177 | println(); 178 | } 179 | 180 | /** 181 | * Groovy left shift operator. 182 | * 183 | * @param o the appended 184 | * @return this object 185 | */ 186 | public final RenderPrintWriter leftShift(Object o) { 187 | if (o instanceof Style) { 188 | try { 189 | out.append((Style)o); 190 | } 191 | catch (InterruptedIOException x) { 192 | Thread.currentThread().interrupt(); 193 | } 194 | catch (IOException x) { 195 | setError(); 196 | } 197 | } else if (o instanceof Decoration) { 198 | try { 199 | out.append((Style.style((Decoration)o))); 200 | } 201 | catch (InterruptedIOException x) { 202 | Thread.currentThread().interrupt(); 203 | } 204 | catch (IOException x) { 205 | setError(); 206 | } 207 | } else if (o instanceof Color) { 208 | try { 209 | out.append(Style.style((Color)o)); 210 | } 211 | catch (InterruptedIOException x) { 212 | Thread.currentThread().interrupt(); 213 | } 214 | catch (IOException x) { 215 | setError(); 216 | } 217 | } else { 218 | print(o); 219 | } 220 | return this; 221 | } 222 | 223 | public final RenderPrintWriter cls() { 224 | try { 225 | out.cls(); 226 | } 227 | catch (InterruptedIOException x) { 228 | Thread.currentThread().interrupt(); 229 | } 230 | catch (IOException x) { 231 | setError(); 232 | } 233 | return this; 234 | } 235 | 236 | @Override 237 | public void println(Object x) { 238 | print(x); 239 | println(); 240 | } 241 | 242 | public void show(Element element) { 243 | element.render(new RenderAppendable(this.out.out)); 244 | } 245 | 246 | @Override 247 | public void print(Object obj) { 248 | if (obj instanceof Element) { 249 | RenderAppendable out = new RenderAppendable(this.out.out); 250 | ((Element)obj).renderer().render(out); 251 | } else { 252 | super.print(obj); 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/RenderWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | import java.io.Closeable; 23 | import java.io.IOException; 24 | import java.io.Writer; 25 | 26 | public class RenderWriter extends Writer implements Screenable { 27 | 28 | /** . */ 29 | final ScreenContext out; 30 | 31 | /** . */ 32 | private final Closeable closeable; 33 | 34 | /** . */ 35 | private boolean closed; 36 | 37 | /** . */ 38 | private boolean empty; 39 | 40 | public RenderWriter(ScreenContext out) throws NullPointerException { 41 | this(out, null); 42 | } 43 | 44 | public RenderWriter(ScreenContext out, Closeable closeable) throws NullPointerException { 45 | if (out == null) { 46 | throw new NullPointerException("No null appendable expected"); 47 | } 48 | 49 | // 50 | this.out = out; 51 | this.empty = true; 52 | this.closeable = closeable; 53 | } 54 | 55 | public boolean isEmpty() { 56 | return empty; 57 | } 58 | 59 | public RenderWriter append(CharSequence s) throws IOException { 60 | empty &= s.length() == 0; 61 | out.append(s); 62 | return this; 63 | } 64 | 65 | public Screenable append(Style style) throws IOException { 66 | out.append(style); 67 | return this; 68 | } 69 | 70 | public Screenable cls() throws IOException { 71 | out.cls(); 72 | return this; 73 | } 74 | 75 | @Override 76 | public void write(char[] cbuf, int off, int len) throws IOException { 77 | if (closed) { 78 | throw new IOException("Already closed"); 79 | } 80 | if (len > 0) { 81 | out.append(new String(cbuf, off, len)); 82 | } 83 | } 84 | 85 | @Override 86 | public void flush() throws IOException { 87 | if (closed) { 88 | throw new IOException("Already closed"); 89 | } 90 | try { 91 | out.flush(); 92 | } 93 | catch (IOException e) { 94 | throw e; 95 | } 96 | catch (Exception e) { 97 | // e.printStackTrace(); 98 | // just swallow ? 99 | } 100 | } 101 | 102 | @Override 103 | public void close() throws IOException { 104 | if (!closed) { 105 | closed = true; 106 | if (closeable != null) { 107 | closeable.close(); 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/Renderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Iterator; 24 | import java.util.ServiceConfigurationError; 25 | import java.util.ServiceLoader; 26 | 27 | import com.taobao.text.ui.LabelElement; 28 | 29 | /** 30 | * Provide a renderable. 31 | */ 32 | public abstract class Renderer { 33 | 34 | /** . */ 35 | private static final Renderer[] renderables; 36 | 37 | static { 38 | ArrayList> tmp = new ArrayList>(); 39 | Iterator i = ServiceLoader.load(Renderer.class).iterator(); 40 | while (i.hasNext()) { 41 | try { 42 | Renderer renderable = i.next(); 43 | tmp.add(renderable); 44 | } 45 | catch (ServiceConfigurationError e) { 46 | // Config error 47 | } 48 | } 49 | renderables = tmp.toArray(new Renderer[tmp.size()]); 50 | } 51 | 52 | public static Renderer ANY = new Renderer() { 53 | @Override 54 | public Class getType() { 55 | return Object.class; 56 | } 57 | 58 | @Override 59 | public LineRenderer renderer(Iterator stream) { 60 | StringBuilder sb = new StringBuilder(); 61 | while (stream.hasNext()) { 62 | Object next = stream.next(); 63 | if (next instanceof CharSequence) { 64 | sb.append((CharSequence)next); 65 | } else { 66 | sb.append(next); 67 | } 68 | } 69 | return new LabelElement(sb.toString()).renderer(); 70 | } 71 | }; 72 | 73 | public static Renderer getRenderable(Class itemType) { 74 | for (Renderer formatter : renderables) { 75 | try { 76 | if (formatter.getType().isAssignableFrom(itemType)) { 77 | return (Renderer)formatter; 78 | } 79 | } 80 | catch (Exception e) { 81 | } 82 | catch (NoClassDefFoundError e) { 83 | } 84 | } 85 | return null; 86 | } 87 | 88 | public abstract Class getType(); 89 | 90 | public abstract LineRenderer renderer(Iterator stream); 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ScreenBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | import java.io.IOException; 23 | import java.io.Serializable; 24 | import java.util.Iterator; 25 | import java.util.LinkedList; 26 | 27 | public class ScreenBuffer implements Iterable, Serializable, Screenable { 28 | 29 | /** . */ 30 | private final LinkedList chunks; 31 | 32 | /** . */ 33 | private Style current; 34 | 35 | /** . */ 36 | private Style next; 37 | 38 | /** Where we flush. */ 39 | private final ScreenContext out; 40 | 41 | public ScreenBuffer() { 42 | this.chunks = new LinkedList(); 43 | this.current = Style.style(); 44 | this.next = Style.style(); 45 | this.out = null; 46 | } 47 | 48 | public ScreenBuffer(ScreenContext out) { 49 | this.chunks = new LinkedList(); 50 | this.current = Style.style(); 51 | this.next = Style.style(); 52 | this.out = out; 53 | } 54 | 55 | public Iterator iterator() { 56 | return chunks.iterator(); 57 | } 58 | 59 | public void format(Format format, Appendable appendable) throws IOException { 60 | format.begin(appendable); 61 | for (Object chunk : this) { 62 | if (chunk instanceof Style) { 63 | format.write((Style)chunk, appendable); 64 | } else if (chunk instanceof CLS) { 65 | format.cls(appendable); 66 | } else if (chunk instanceof Character) { 67 | format.write(((Character) chunk).charValue(), appendable); 68 | } else { 69 | format.write((CharSequence)chunk, appendable); 70 | } 71 | } 72 | //如果next是rest的话,要恢复style 73 | if(this.next != null && next.equals(Style.reset)){ 74 | format.write(next, appendable); 75 | } 76 | format.end(appendable); 77 | } 78 | 79 | public ScreenBuffer append(Iterable data) throws NullPointerException { 80 | for (Object o : data) { 81 | append(o); 82 | } 83 | return this; 84 | } 85 | 86 | public ScreenBuffer append(Object... data) throws NullPointerException { 87 | for (Object o : data) { 88 | append(o); 89 | } 90 | return this; 91 | } 92 | 93 | public ScreenBuffer cls() { 94 | chunks.addLast(CLS.INSTANCE); 95 | return this; 96 | } 97 | 98 | public ScreenBuffer append(Style style) throws NullPointerException { 99 | next = next.merge(style); 100 | return this; 101 | } 102 | 103 | @Override 104 | public ScreenBuffer append(char c) { 105 | if (!next.equals(current)) { 106 | if (!Style.style().equals(next)) { 107 | chunks.addLast(next); 108 | } 109 | current = next; 110 | next = Style.style(); 111 | } 112 | chunks.addLast(c); 113 | return this; 114 | } 115 | 116 | public ScreenBuffer append(CharSequence s) { 117 | if (s.length() > 0) { 118 | if (!next.equals(current)) { 119 | if (!Style.style().equals(next)) { 120 | chunks.addLast(next); 121 | } 122 | current = next; 123 | next = Style.style(); 124 | } 125 | chunks.addLast(s); 126 | } 127 | return this; 128 | } 129 | 130 | public ScreenBuffer append(CharSequence s, int start, int end) { 131 | if (end != start) { 132 | if (start != 0 || end != s.length()) { 133 | s = s.subSequence(start, end); 134 | } 135 | append(s); 136 | } 137 | return this; 138 | } 139 | 140 | public void flush() throws IOException { 141 | if (out != null) { 142 | for (Object chunk : chunks) { 143 | if (chunk instanceof CLS) { 144 | out.cls(); 145 | } else if (chunk instanceof CharSequence) { 146 | out.append((CharSequence)chunk); 147 | } else if (chunk instanceof Character) { 148 | out.append((Character)chunk); 149 | } else { 150 | out.append((Style)chunk); 151 | } 152 | } 153 | } 154 | chunks.clear(); 155 | if (out != null) { 156 | out.flush(); 157 | } 158 | } 159 | 160 | public ScreenBuffer append(ScreenBuffer s) throws NullPointerException { 161 | for (Object chunk : s.chunks) { 162 | append(chunk); 163 | } 164 | if (s.next != null && !s.next.equals(Style.style())) { 165 | append(s.next); 166 | } 167 | return this; 168 | } 169 | 170 | public ScreenBuffer append(Object o) throws NullPointerException { 171 | if (o == null) { 172 | throw new NullPointerException("No null accepted"); 173 | } 174 | if (o instanceof ScreenBuffer) { 175 | append((ScreenBuffer)o); 176 | } else if (o instanceof Style) { 177 | append((Style)o); 178 | } else if (o instanceof CharSequence){ 179 | append(((CharSequence)o)); 180 | } else if (o instanceof Character){ 181 | append(((Character) o).charValue()); 182 | } else if (o instanceof CLS) { 183 | cls(); 184 | } else { 185 | append(o.toString()); 186 | } 187 | return this; 188 | } 189 | 190 | public boolean contains(Object o) { 191 | return toString().contains(o.toString()); 192 | } 193 | 194 | public boolean isEmpty() { 195 | return chunks.isEmpty(); 196 | } 197 | 198 | public void clear() { 199 | chunks.clear(); 200 | } 201 | 202 | @Override 203 | public int hashCode() { 204 | return toString().hashCode(); 205 | } 206 | 207 | @Override 208 | public boolean equals(Object obj) { 209 | if (obj == this) { 210 | return true; 211 | } 212 | if (obj instanceof ScreenBuffer) { 213 | ScreenBuffer that = (ScreenBuffer)obj; 214 | return toString().equals(that.toString()); 215 | } 216 | return false; 217 | } 218 | 219 | @Override 220 | public String toString() { 221 | StringBuilder sb = new StringBuilder(); 222 | try { 223 | format(Format.TEXT, sb); 224 | } 225 | catch (IOException ignore) { 226 | } 227 | return sb.toString(); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ScreenContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * The screen context extends the {@link Screenable} and add information about the screen. 26 | */ 27 | public interface ScreenContext extends Screenable { 28 | 29 | /** 30 | * Returns the screen width in chars. When the value is not positive it means 31 | * the value could not be determined. 32 | * 33 | * @return the term width 34 | */ 35 | int getWidth(); 36 | 37 | /** 38 | * Returns the screen height in chars. When the value is not positive it means 39 | * the value could not be determined. 40 | * 41 | * @return the term height 42 | */ 43 | int getHeight(); 44 | 45 | /** 46 | * Flush the stream. 47 | * 48 | * @throws IOException any io exception 49 | */ 50 | void flush() throws IOException; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ScreenContextConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text; 21 | 22 | import java.io.IOException; 23 | import java.util.LinkedList; 24 | 25 | import com.taobao.text.stream.Consumer; 26 | 27 | /** 28 | * A Consumer<Object> that renders the object stream to a {@link ScreenContext}. 29 | */ 30 | public class ScreenContextConsumer implements Consumer { 31 | 32 | /** Buffers objects of the same kind. */ 33 | private final LinkedList buffer = new LinkedList(); 34 | 35 | /** . */ 36 | private Renderer renderable = null; 37 | 38 | /** . */ 39 | private final RenderAppendable out; 40 | 41 | public ScreenContextConsumer(ScreenContext out) { 42 | this.out = new RenderAppendable(out); 43 | } 44 | 45 | public Class getConsumedType() { 46 | return Object.class; 47 | } 48 | 49 | public void provide(Object element) throws IOException { 50 | Renderer current = Renderer.getRenderable(element.getClass()); 51 | if (current == null) { 52 | send(); 53 | if (element instanceof CharSequence) { 54 | out.append((CharSequence)element); 55 | } else if (element instanceof CLS) { 56 | out.cls(); 57 | } else if (element instanceof Style) { 58 | out.append((Style)element); 59 | } else { 60 | out.append(element.toString()); 61 | } 62 | } else { 63 | if (renderable != null && !current.equals(renderable)) { 64 | send(); 65 | } 66 | buffer.addLast(element); 67 | renderable = current; 68 | } 69 | } 70 | 71 | public void flush() throws IOException { 72 | send(); 73 | out.flush(); 74 | } 75 | 76 | public void send() throws IOException { 77 | if (buffer.size() > 0) { 78 | LineRenderer renderer = renderable.renderer(buffer.iterator()); 79 | renderer.render(out); 80 | buffer.clear(); 81 | renderable = null; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/Screenable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | package com.taobao.text; 20 | 21 | import java.io.IOException; 22 | 23 | /** 24 | * The interface for pushing data to a screen. 25 | * 26 | * @author Julien Viet 27 | */ 28 | public interface Screenable extends Appendable { 29 | 30 | Screenable append(Style style) throws IOException; 31 | 32 | Screenable cls() throws IOException; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/lang/HighLightTheme.java: -------------------------------------------------------------------------------- 1 | package com.taobao.text.lang; 2 | 3 | import java.io.InputStream; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Modifier; 6 | import java.net.URL; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import javax.xml.parsers.DocumentBuilder; 11 | import javax.xml.parsers.DocumentBuilderFactory; 12 | 13 | import org.fife.ui.rsyntaxtextarea.TokenTypes; 14 | import org.w3c.dom.Document; 15 | import org.w3c.dom.NamedNodeMap; 16 | import org.w3c.dom.Node; 17 | import org.w3c.dom.NodeList; 18 | 19 | import com.taobao.text.Color; 20 | import com.taobao.text.Style; 21 | import com.taobao.text.Style.Composite; 22 | 23 | /** 24 | * 语法高亮的配置,即每一种syntax对应的颜色配置 25 | * 26 | * @author duanling 2015年12月2日 下午3:17:14 27 | * 28 | */ 29 | public class HighLightTheme { 30 | 31 | Map styleMap = new HashMap(); 32 | 33 | static Map tokenTypeMap; 34 | 35 | static final String defaulConfigPath = "com/taobao/text/ui/themes/default.xml"; 36 | 37 | private static volatile HighLightTheme defaultTheme = null; 38 | 39 | static { 40 | tokenTypeMap = new HashMap(); 41 | // 获取当前支持的所有的token type 42 | Field[] declaredFields = TokenTypes.class.getDeclaredFields(); 43 | for (Field field : declaredFields) { 44 | if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { 45 | if (field.getType().equals(int.class) && Modifier.isStatic(field.getModifiers())) { 46 | try { 47 | tokenTypeMap.put(field.getName(), field.getInt(null)); 48 | } catch (Exception e) { 49 | // ignore 50 | } 51 | } 52 | } 53 | } 54 | } 55 | 56 | public static HighLightTheme load(URL url) throws Exception { 57 | InputStream openStream = null; 58 | try { 59 | openStream = url.openStream(); 60 | 61 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 62 | DocumentBuilder db = dbf.newDocumentBuilder(); 63 | 64 | Document document = db.parse(openStream); 65 | NodeList elements = document.getElementsByTagName("style"); 66 | 67 | HighLightTheme theme = new HighLightTheme(); 68 | 69 | for (int i = 0; i < elements.getLength(); ++i) { 70 | Node item = elements.item(i); 71 | NamedNodeMap attributes = item.getAttributes(); 72 | // token="RESERVED_WORD" fg="blue" bold="true" 73 | 74 | Node token = attributes.getNamedItem("token"); 75 | String tokenValue = token.getNodeValue(); 76 | Integer tokenType = tokenTypeMap.get(tokenValue); 77 | if (tokenType == null) { 78 | // skip unknown token type 79 | continue; 80 | } 81 | 82 | Color fgColor = null; 83 | Color bgColor = null; 84 | Boolean boldValue = null; 85 | 86 | Node fg = attributes.getNamedItem("fg"); 87 | if (fg != null) { 88 | fgColor = Color.valueOf(fg.getNodeValue()); 89 | } 90 | 91 | Node bg = attributes.getNamedItem("bg"); 92 | if (bg != null) { 93 | bgColor = Color.valueOf(bg.getNodeValue()); 94 | } 95 | 96 | Node bold = attributes.getNamedItem("bold"); 97 | if (bold != null) { 98 | boldValue = Boolean.parseBoolean(bold.getNodeValue()); 99 | } 100 | 101 | theme.setStyle(tokenType, Composite.style(boldValue, null, null, fgColor, bgColor)); 102 | } 103 | 104 | return theme; 105 | 106 | } finally { 107 | if (openStream != null) { 108 | openStream.close(); 109 | } 110 | } 111 | } 112 | 113 | public static HighLightTheme defaultTheme() { 114 | if (defaultTheme == null) { 115 | URL resource = HighLightTheme.class.getClassLoader().getResource(defaulConfigPath); 116 | try { 117 | defaultTheme = load(resource); 118 | } catch (Exception e) { 119 | throw new RuntimeException("load text.ui theme error!", e); 120 | } 121 | } 122 | 123 | return defaultTheme; 124 | } 125 | 126 | public Style getStyle(int tokenType) { 127 | Style style = styleMap.get(tokenType); 128 | if (style != null) { 129 | return style; 130 | } 131 | 132 | return Style.style(); 133 | } 134 | 135 | public void setStyle(int tokenType, Style style) { 136 | styleMap.put(tokenType, style); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/lang/LangRenderUtil.java: -------------------------------------------------------------------------------- 1 | package com.taobao.text.lang; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Scanner; 8 | 9 | import javax.swing.text.Segment; 10 | 11 | import org.fife.ui.rsyntaxtextarea.Token; 12 | import org.fife.ui.rsyntaxtextarea.TokenMaker; 13 | import org.fife.ui.rsyntaxtextarea.TokenTypes; 14 | import org.fife.ui.rsyntaxtextarea.modes.ActionScriptTokenMaker; 15 | import org.fife.ui.rsyntaxtextarea.modes.AssemblerX86TokenMaker; 16 | import org.fife.ui.rsyntaxtextarea.modes.BBCodeTokenMaker; 17 | import org.fife.ui.rsyntaxtextarea.modes.CPlusPlusTokenMaker; 18 | import org.fife.ui.rsyntaxtextarea.modes.CSSTokenMaker; 19 | import org.fife.ui.rsyntaxtextarea.modes.CSharpTokenMaker; 20 | import org.fife.ui.rsyntaxtextarea.modes.CTokenMaker; 21 | import org.fife.ui.rsyntaxtextarea.modes.ClojureTokenMaker; 22 | import org.fife.ui.rsyntaxtextarea.modes.DTokenMaker; 23 | import org.fife.ui.rsyntaxtextarea.modes.DartTokenMaker; 24 | import org.fife.ui.rsyntaxtextarea.modes.DelphiTokenMaker; 25 | import org.fife.ui.rsyntaxtextarea.modes.DtdTokenMaker; 26 | import org.fife.ui.rsyntaxtextarea.modes.FortranTokenMaker; 27 | import org.fife.ui.rsyntaxtextarea.modes.GroovyTokenMaker; 28 | import org.fife.ui.rsyntaxtextarea.modes.HTMLTokenMaker; 29 | import org.fife.ui.rsyntaxtextarea.modes.HtaccessTokenMaker; 30 | import org.fife.ui.rsyntaxtextarea.modes.JSPTokenMaker; 31 | import org.fife.ui.rsyntaxtextarea.modes.JavaScriptTokenMaker; 32 | import org.fife.ui.rsyntaxtextarea.modes.JavaTokenMaker; 33 | import org.fife.ui.rsyntaxtextarea.modes.JshintrcTokenMaker; 34 | import org.fife.ui.rsyntaxtextarea.modes.JsonTokenMaker; 35 | import org.fife.ui.rsyntaxtextarea.modes.LatexTokenMaker; 36 | import org.fife.ui.rsyntaxtextarea.modes.LessTokenMaker; 37 | import org.fife.ui.rsyntaxtextarea.modes.LispTokenMaker; 38 | import org.fife.ui.rsyntaxtextarea.modes.LuaTokenMaker; 39 | import org.fife.ui.rsyntaxtextarea.modes.MakefileTokenMaker; 40 | import org.fife.ui.rsyntaxtextarea.modes.MxmlTokenMaker; 41 | import org.fife.ui.rsyntaxtextarea.modes.NSISTokenMaker; 42 | import org.fife.ui.rsyntaxtextarea.modes.PHPTokenMaker; 43 | import org.fife.ui.rsyntaxtextarea.modes.PerlTokenMaker; 44 | import org.fife.ui.rsyntaxtextarea.modes.PlainTextTokenMaker; 45 | import org.fife.ui.rsyntaxtextarea.modes.PropertiesFileTokenMaker; 46 | import org.fife.ui.rsyntaxtextarea.modes.PythonTokenMaker; 47 | import org.fife.ui.rsyntaxtextarea.modes.RubyTokenMaker; 48 | import org.fife.ui.rsyntaxtextarea.modes.SASTokenMaker; 49 | import org.fife.ui.rsyntaxtextarea.modes.SQLTokenMaker; 50 | import org.fife.ui.rsyntaxtextarea.modes.ScalaTokenMaker; 51 | import org.fife.ui.rsyntaxtextarea.modes.TclTokenMaker; 52 | import org.fife.ui.rsyntaxtextarea.modes.UnixShellTokenMaker; 53 | import org.fife.ui.rsyntaxtextarea.modes.VisualBasicTokenMaker; 54 | import org.fife.ui.rsyntaxtextarea.modes.WindowsBatchTokenMaker; 55 | import org.fife.ui.rsyntaxtextarea.modes.XMLTokenMaker; 56 | 57 | import com.taobao.text.Style; 58 | 59 | /** 60 | * 61 | * @author duanling 2015年12月3日 上午11:38:55 62 | * 63 | */ 64 | public class LangRenderUtil { 65 | public static final String plain = "plain"; 66 | public static final String actionscript = "actionscript"; 67 | public static final String asm = "asm"; 68 | public static final String bbcode = "bbcode"; 69 | public static final String c = "c"; 70 | public static final String clojure = "clojure"; 71 | public static final String cpp = "cpp"; 72 | public static final String cs = "cs"; 73 | public static final String css = "css"; 74 | public static final String d = "d"; 75 | public static final String dart = "dart"; 76 | public static final String delphi = "delphi"; 77 | public static final String dtd = "dtd"; 78 | public static final String fortran = "fortran"; 79 | public static final String groovy = "groovy"; 80 | public static final String htaccess = "htaccess"; 81 | public static final String html = "html"; 82 | public static final String java = "java"; 83 | public static final String javascript = "javascript"; 84 | public static final String jshintrc = "jshintrc"; 85 | public static final String json = "json"; 86 | public static final String jsp = "jsp"; 87 | public static final String latex = "latex"; 88 | public static final String less = "less"; 89 | public static final String lisp = "lisp"; 90 | public static final String lua = "lua"; 91 | public static final String makefile = "makefile"; 92 | public static final String mxml = "mxml"; 93 | public static final String nsis = "nsis"; 94 | public static final String perl = "perl"; 95 | public static final String php = "php"; 96 | public static final String properties = "properties"; 97 | public static final String python = "python"; 98 | public static final String ruby = "ruby"; 99 | public static final String sas = "sas"; 100 | public static final String scala = "scala"; 101 | public static final String sql = "sql"; 102 | public static final String tcl = "tcl"; 103 | public static final String unix = "unix"; 104 | public static final String vb = "vb"; 105 | public static final String bat = "bat"; 106 | public static final String xml = "xml"; 107 | 108 | private static Map> tokenMakerMap = new HashMap>(); 109 | 110 | static { 111 | tokenMakerMap.put(plain, PlainTextTokenMaker.class); 112 | tokenMakerMap.put(actionscript, ActionScriptTokenMaker.class); 113 | tokenMakerMap.put(asm, AssemblerX86TokenMaker.class); 114 | tokenMakerMap.put(bbcode, BBCodeTokenMaker.class); 115 | tokenMakerMap.put(c, CTokenMaker.class); 116 | tokenMakerMap.put(clojure, ClojureTokenMaker.class); 117 | tokenMakerMap.put(cpp, CPlusPlusTokenMaker.class); 118 | tokenMakerMap.put(cs, CSharpTokenMaker.class); 119 | tokenMakerMap.put(css, CSSTokenMaker.class); 120 | tokenMakerMap.put(d, DTokenMaker.class); 121 | tokenMakerMap.put(dart, DartTokenMaker.class); 122 | tokenMakerMap.put(delphi, DelphiTokenMaker.class); 123 | tokenMakerMap.put(dtd, DtdTokenMaker.class); 124 | tokenMakerMap.put(fortran, FortranTokenMaker.class); 125 | tokenMakerMap.put(groovy, GroovyTokenMaker.class); 126 | tokenMakerMap.put(htaccess, HtaccessTokenMaker.class); 127 | tokenMakerMap.put(html, HTMLTokenMaker.class); 128 | tokenMakerMap.put(java, JavaTokenMaker.class); 129 | tokenMakerMap.put(javascript, JavaScriptTokenMaker.class); 130 | tokenMakerMap.put(jshintrc, JshintrcTokenMaker.class); 131 | tokenMakerMap.put(json, JsonTokenMaker.class); 132 | tokenMakerMap.put(jsp, JSPTokenMaker.class); 133 | tokenMakerMap.put(latex, LatexTokenMaker.class); 134 | tokenMakerMap.put(less, LessTokenMaker.class); 135 | tokenMakerMap.put(lisp, LispTokenMaker.class); 136 | tokenMakerMap.put(lua, LuaTokenMaker.class); 137 | tokenMakerMap.put(makefile, MakefileTokenMaker.class); 138 | tokenMakerMap.put(mxml, MxmlTokenMaker.class); 139 | tokenMakerMap.put(nsis, NSISTokenMaker.class); 140 | tokenMakerMap.put(perl, PerlTokenMaker.class); 141 | tokenMakerMap.put(php, PHPTokenMaker.class); 142 | tokenMakerMap.put(properties, PropertiesFileTokenMaker.class); 143 | tokenMakerMap.put(python, PythonTokenMaker.class); 144 | tokenMakerMap.put(ruby, RubyTokenMaker.class); 145 | tokenMakerMap.put(sas, SASTokenMaker.class); 146 | tokenMakerMap.put(scala, ScalaTokenMaker.class); 147 | tokenMakerMap.put(sql, SQLTokenMaker.class); 148 | tokenMakerMap.put(tcl, TclTokenMaker.class); 149 | tokenMakerMap.put(unix, UnixShellTokenMaker.class); 150 | tokenMakerMap.put(vb, VisualBasicTokenMaker.class); 151 | tokenMakerMap.put(bat, WindowsBatchTokenMaker.class); 152 | tokenMakerMap.put(xml, XMLTokenMaker.class); 153 | } 154 | 155 | static TokenMaker createTokenMaker(String lang) { 156 | Class clazz = tokenMakerMap.get(lang); 157 | if (clazz == null) { 158 | clazz = JavaTokenMaker.class; 159 | } 160 | try { 161 | return (TokenMaker) clazz.newInstance(); 162 | } catch (Exception e) { 163 | throw new RuntimeException("construct TokenMaker error!, lang:" + lang, e); 164 | } 165 | } 166 | 167 | static List lines(String multilines) { 168 | List result = new ArrayList(); 169 | Scanner scanner = new Scanner(multilines); 170 | try { 171 | while (scanner.hasNextLine()) { 172 | String line = scanner.nextLine(); 173 | result.add(line); 174 | } 175 | } finally { 176 | scanner.close(); 177 | } 178 | return result; 179 | } 180 | 181 | static public String render(String code, String lang, HighLightTheme theme) { 182 | StringBuilder sb = new StringBuilder(8192); 183 | 184 | TokenMaker tm = createTokenMaker(lang); 185 | 186 | int previousLineTokenType = TokenTypes.NULL; 187 | for (String line : lines(code)) { 188 | Segment segment = new Segment(line.toCharArray(), 0, line.length()); 189 | 190 | Token token = tm.getTokenList(segment, previousLineTokenType, 0); 191 | 192 | while (token != null) { 193 | 194 | int type = token.getType(); 195 | if (type < 0) { 196 | break; 197 | } 198 | previousLineTokenType = type; 199 | 200 | Style style = theme.getStyle(type); 201 | if (style != null) { 202 | sb.append(style.toAnsiSequence()); 203 | } 204 | String lexeme = token.getLexeme(); 205 | if (lexeme != null) { 206 | sb.append(token.getLexeme()); 207 | } 208 | 209 | if (style != null) { 210 | sb.append(Style.reset.toAnsiSequence()); 211 | } 212 | 213 | token = token.getNextToken(); 214 | } 215 | 216 | sb.append('\n'); 217 | } 218 | 219 | return sb.toString(); 220 | } 221 | 222 | static public String render(String code) { 223 | return render(code, "java"); 224 | } 225 | 226 | static public String render(String code, String lang) { 227 | return render(code, lang, HighLightTheme.defaultTheme()); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/BindingRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | package com.taobao.text.renderers; 20 | 21 | import com.taobao.text.LineRenderer; 22 | import com.taobao.text.Renderer; 23 | import com.taobao.text.ui.LabelElement; 24 | import com.taobao.text.ui.RowElement; 25 | import com.taobao.text.ui.TableElement; 26 | 27 | import java.util.Iterator; 28 | 29 | /** 30 | * @author Alain Defrance 31 | */ 32 | public class BindingRenderer extends Renderer { 33 | 34 | @Override 35 | public Class getType() { 36 | return BindingData.class; 37 | } 38 | 39 | @Override 40 | public LineRenderer renderer(Iterator stream) { 41 | 42 | TableElement table = new TableElement(); 43 | table.setRightCellPadding(1); 44 | RowElement header = new RowElement(true); 45 | header.add("NAME"); 46 | table.add(header); 47 | 48 | while (stream.hasNext()) { 49 | BindingData binding = stream.next(); 50 | 51 | RowElement row = new RowElement(); 52 | 53 | row.add(binding.name); 54 | 55 | if (binding.verbose) { 56 | row.add(new LabelElement(binding.type)); 57 | if (header.getSize() == 1) { 58 | header.add("CLASS"); 59 | } 60 | } 61 | 62 | table.add(row); 63 | 64 | } 65 | 66 | return table.renderer(); 67 | } 68 | 69 | public static class BindingData { 70 | 71 | public final String name; 72 | public final String type; 73 | public final Object instance; 74 | public final Boolean verbose; 75 | 76 | public BindingData(String name, String type, Object instance, Boolean verbose) { 77 | this.name = name; 78 | this.type = type; 79 | this.instance = instance; 80 | this.verbose = (verbose != null ? verbose : false); 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/EntityTypeRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.renderers; 21 | 22 | import com.taobao.text.LineRenderer; 23 | import com.taobao.text.Renderer; 24 | import com.taobao.text.ui.*; 25 | 26 | import java.util.*; 27 | 28 | /** 29 | * @author Alain Defrance 30 | */ 31 | public class EntityTypeRenderer extends Renderer { 32 | 33 | @Override 34 | public Class getType() { 35 | return EntityTypeRenderer.EntityTypeData.class; 36 | } 37 | 38 | @Override 39 | public LineRenderer renderer(Iterator stream) { 40 | 41 | TableElement table = new TableElement(); 42 | 43 | table.setRightCellPadding(1); 44 | 45 | while (stream.hasNext()) { 46 | EntityTypeData entityTypeData = stream.next(); 47 | 48 | if (!entityTypeData.verbose) { 49 | if (table.getRows().size() == 0) { 50 | RowElement header = new RowElement(true); 51 | header.add("NAME", "TYPE"); 52 | table.add(header); 53 | } 54 | RowElement row = new RowElement(); 55 | row.add(entityTypeData.name, entityTypeData.type); 56 | table.add(row); 57 | } else { 58 | table.setColumnLayout(Layout.weighted(1)); 59 | RowElement name = new RowElement(); 60 | name.add("Name : " + entityTypeData.name); 61 | table.add(name); 62 | RowElement type = new RowElement(); 63 | type.add("Type : " + entityTypeData.type); 64 | table.add(type); 65 | RowElement mapping = new RowElement(); 66 | mapping.add("Mapping : " + entityTypeData.mapping); 67 | table.add(mapping); 68 | 69 | if (entityTypeData.attributes.size() > 0) { 70 | RowElement attributesLabel = new RowElement(); 71 | attributesLabel.add("Attributes : "); 72 | table.add(attributesLabel); 73 | 74 | TableElement attributeTable = new TableElement(); 75 | attributeTable.setRightCellPadding(1); 76 | RowElement attributeRowHeader = new RowElement(true); 77 | attributeRowHeader.add("NAME", "TYPE", "ASSOCIATION", "COLLECTION", "MAPPING"); 78 | attributeTable.add(attributeRowHeader); 79 | 80 | for (AttributeData attributes : entityTypeData.attributes) { 81 | RowElement row = new RowElement(); 82 | row.add(attributes.name, attributes.type, "" + attributes.association, "" + attributes.collection, attributes.mapping); 83 | attributeTable.add(row); 84 | } 85 | 86 | RowElement attributesRow = new RowElement(); 87 | attributesRow.add(attributeTable); 88 | table.add(attributesRow); 89 | 90 | } 91 | } 92 | 93 | } 94 | 95 | return table.renderer(); 96 | 97 | } 98 | 99 | public static class EntityTypeData { 100 | 101 | public final String name; 102 | public final String type; 103 | public final String mapping; 104 | public final boolean verbose; 105 | public final List attributes; 106 | 107 | public EntityTypeData(String name, String type, String mapping) { 108 | this(name, type, mapping, false); 109 | } 110 | 111 | public EntityTypeData(String name, String type, String mapping, boolean verbose) { 112 | this.name = name; 113 | this.type = type; 114 | this.mapping = mapping; 115 | this.verbose = verbose; 116 | this.attributes = new ArrayList(); 117 | } 118 | 119 | public void add(AttributeData d) { 120 | attributes.add(d); 121 | } 122 | 123 | } 124 | 125 | public static class AttributeData { 126 | public final String name; 127 | public final String type; 128 | public final Boolean association; 129 | public final Boolean collection; 130 | public final String mapping; 131 | 132 | public AttributeData(String name, String type, Boolean association, Boolean collection, String mapping) { 133 | this.name = name; 134 | this.type = type; 135 | this.association = (association != null ? association : false); 136 | this.collection = (collection != null ? collection : false); 137 | this.mapping = mapping; 138 | } 139 | 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/FileRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.renderers; 21 | 22 | import com.taobao.text.LineRenderer; 23 | import com.taobao.text.Renderer; 24 | import com.taobao.text.ui.LabelElement; 25 | import com.taobao.text.ui.Overflow; 26 | import com.taobao.text.ui.RowElement; 27 | import com.taobao.text.ui.TableElement; 28 | import com.taobao.text.util.Utils; 29 | 30 | import java.io.File; 31 | import java.text.SimpleDateFormat; 32 | import java.util.Collections; 33 | import java.util.Date; 34 | import java.util.Iterator; 35 | import java.util.List; 36 | 37 | public class FileRenderer extends Renderer { 38 | 39 | @Override 40 | public Class getType() { 41 | return File.class; 42 | } 43 | 44 | @Override 45 | public LineRenderer renderer(Iterator stream) { 46 | 47 | // 48 | List files = Utils.list(stream); 49 | Collections.sort(files); 50 | TableElement table = new TableElement().overflow(Overflow.WRAP).rightCellPadding(1); 51 | SimpleDateFormat format = new SimpleDateFormat("MMM dd HH:mm"); 52 | Date date = new Date(); 53 | 54 | // 55 | for (File file : files) { 56 | String mode = (file.isDirectory() ? "d" : "-") 57 | + (file.canRead() ? "r" : "-") 58 | + (file.canWrite() ? "w" : "-") 59 | + (file.canExecute() ? "x" : "-") 60 | ; 61 | String length = Long.toString(file.length()); 62 | date.setTime(file.lastModified()); 63 | String lastModified = format.format(date); 64 | table.row( 65 | mode, 66 | length, 67 | lastModified, 68 | file.getName() 69 | ); 70 | } 71 | 72 | // 73 | return table.renderer(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/LogRecordRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | package com.taobao.text.renderers; 20 | 21 | import com.taobao.text.Color; 22 | import com.taobao.text.LineRenderer; 23 | import com.taobao.text.Renderer; 24 | import com.taobao.text.ui.LabelElement; 25 | import com.taobao.text.util.BaseIterator; 26 | 27 | import java.util.Iterator; 28 | import java.util.logging.Level; 29 | import java.util.logging.LogRecord; 30 | import java.util.logging.SimpleFormatter; 31 | 32 | /** 33 | * A renderer for {@link java.util.logging.LogRecord} objects based on the {@link java.util.logging.SimpleFormatter} 34 | * formatter. 35 | * 36 | * @author Julien Viet 37 | */ 38 | public class LogRecordRenderer extends Renderer { 39 | 40 | @Override 41 | public Class getType() { 42 | return LogRecord.class; 43 | } 44 | 45 | @Override 46 | public LineRenderer renderer(final Iterator stream) { 47 | return LineRenderer.vertical(new Iterable() { 48 | @Override 49 | public Iterator iterator() { 50 | return new BaseIterator() { 51 | final SimpleFormatter formatter = new SimpleFormatter(); 52 | @Override 53 | public boolean hasNext() { 54 | return stream.hasNext(); 55 | } 56 | @Override 57 | public LineRenderer next() { 58 | LogRecord record = stream.next(); 59 | String line = formatter.format(record); 60 | Color color; 61 | if (record.getLevel() == Level.SEVERE) { 62 | color = Color.red; 63 | } else if (record.getLevel() == Level.WARNING) { 64 | color = Color.yellow; 65 | } else if (record.getLevel() == Level.INFO) { 66 | color = Color.green; 67 | } else { 68 | color = Color.blue; 69 | } 70 | return new LabelElement(line).style(color.fg()).renderer(); 71 | } 72 | }; 73 | } 74 | }); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/LoggerRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.renderers; 21 | 22 | import com.taobao.text.Color; 23 | import com.taobao.text.Decoration; 24 | import com.taobao.text.LineRenderer; 25 | import com.taobao.text.Renderer; 26 | import com.taobao.text.ui.LabelElement; 27 | import com.taobao.text.ui.RowElement; 28 | import com.taobao.text.ui.TableElement; 29 | 30 | import java.util.Iterator; 31 | import java.util.logging.Level; 32 | import java.util.logging.Logger; 33 | 34 | public class LoggerRenderer extends Renderer { 35 | 36 | @Override 37 | public Class getType() { 38 | return Logger.class; 39 | } 40 | 41 | @Override 42 | public LineRenderer renderer(Iterator stream) { 43 | TableElement table = new TableElement(); 44 | 45 | // Header 46 | table.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("NAME", "LEVEL")); 47 | 48 | // 49 | while (stream.hasNext()) { 50 | Logger logger = stream.next(); 51 | 52 | // Determine level 53 | String level; 54 | if (logger.isLoggable(Level.FINER)) { 55 | level = "TRACE"; 56 | } else if (logger.isLoggable(Level.FINE)) { 57 | level = "DEBUG"; 58 | } else if (logger.isLoggable(Level.INFO)) { 59 | level = "INFO"; 60 | } else if (logger.isLoggable(Level.WARNING)) { 61 | level = "WARN"; 62 | } else if (logger.isLoggable(Level.SEVERE)) { 63 | level = "ERROR"; 64 | } else { 65 | level = "UNKNOWN"; 66 | } 67 | 68 | // 69 | table.row(logger.getName(), level); 70 | } 71 | 72 | // 73 | return table.renderer(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/MBeanInfoRenderer.java: -------------------------------------------------------------------------------- 1 | package com.taobao.text.renderers; 2 | 3 | import com.taobao.text.Color; 4 | import com.taobao.text.Decoration; 5 | import com.taobao.text.LineRenderer; 6 | import com.taobao.text.Renderer; 7 | import com.taobao.text.ui.LabelElement; 8 | import com.taobao.text.ui.Overflow; 9 | import com.taobao.text.ui.RowElement; 10 | import com.taobao.text.ui.TableElement; 11 | import com.taobao.text.ui.TreeElement; 12 | 13 | import javax.management.Descriptor; 14 | import javax.management.MBeanAttributeInfo; 15 | import javax.management.MBeanInfo; 16 | import javax.management.MBeanOperationInfo; 17 | import javax.management.MBeanParameterInfo; 18 | import java.util.ArrayList; 19 | import java.util.Iterator; 20 | import java.util.List; 21 | 22 | /** 23 | * @author Julien Viet 24 | */ 25 | public class MBeanInfoRenderer extends Renderer { 26 | 27 | @Override 28 | public Class getType() { 29 | return MBeanInfo.class; 30 | } 31 | 32 | @Override 33 | public LineRenderer renderer(Iterator stream) { 34 | 35 | List renderers = new ArrayList(); 36 | 37 | while (stream.hasNext()) { 38 | MBeanInfo info = stream.next(); 39 | 40 | // 41 | TreeElement root = new TreeElement(info.getClassName()); 42 | 43 | // Descriptor 44 | TableElement descriptor = new TableElement(). 45 | overflow(Overflow.HIDDEN). 46 | rightCellPadding(1); 47 | Descriptor descriptorInfo = info.getDescriptor(); 48 | if (descriptorInfo != null) { 49 | for (String fieldName : descriptorInfo.getFieldNames()) { 50 | String fieldValue = String.valueOf(descriptorInfo.getFieldValue(fieldName)); 51 | descriptor.row(fieldName, fieldValue); 52 | } 53 | } 54 | 55 | // Attributes 56 | TableElement attributes = new TableElement(). 57 | overflow(Overflow.HIDDEN). 58 | rightCellPadding(1). 59 | add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("NAME", "TYPE", "DESCRIPTION")); 60 | for (MBeanAttributeInfo attributeInfo : info.getAttributes()) { 61 | attributes.row(attributeInfo.getName(), attributeInfo.getType(), attributeInfo.getDescription()); 62 | } 63 | 64 | // Operations 65 | TreeElement operations = new TreeElement("Operations"); 66 | for (MBeanOperationInfo operationInfo : info.getOperations()) { 67 | TableElement signature = new TableElement(). 68 | overflow(Overflow.HIDDEN). 69 | rightCellPadding(1); 70 | MBeanParameterInfo[] parameterInfos = operationInfo.getSignature(); 71 | for (MBeanParameterInfo parameterInfo : parameterInfos) { 72 | signature.row(parameterInfo.getName(), parameterInfo.getType(), parameterInfo.getDescription()); 73 | } 74 | TreeElement operation = new TreeElement(operationInfo.getName()); 75 | String impact; 76 | switch (operationInfo.getImpact()) { 77 | case MBeanOperationInfo.ACTION: 78 | impact = "ACTION"; 79 | break; 80 | case MBeanOperationInfo.INFO: 81 | impact = "INFO"; 82 | break; 83 | case MBeanOperationInfo.ACTION_INFO: 84 | impact = "ACTION_INFO"; 85 | break; 86 | default: 87 | impact = "UNKNOWN"; 88 | } 89 | operation.addChild(new TableElement(). 90 | add( 91 | new RowElement().add("Type: ", operationInfo.getReturnType()), 92 | new RowElement().add("Description: ", operationInfo.getDescription()), 93 | new RowElement().add("Impact: ", impact), 94 | new RowElement().add(new LabelElement("Signature: "), signature) 95 | ) 96 | ); 97 | 98 | operations.addChild(operation); 99 | } 100 | 101 | // 102 | root.addChild( 103 | new TableElement().leftCellPadding(1).overflow(Overflow.HIDDEN). 104 | row("ClassName", info.getClassName()). 105 | row("Description", info.getDescription() 106 | ) 107 | ); 108 | root.addChild(new TreeElement("Descriptor").addChild(descriptor)); 109 | root.addChild(new TreeElement("Attributes").addChild(attributes)); 110 | root.addChild(operations); 111 | 112 | // 113 | renderers.add(root.renderer()); 114 | } 115 | 116 | 117 | 118 | 119 | return LineRenderer.vertical(renderers); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/MapRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.renderers; 21 | 22 | import com.taobao.text.Color; 23 | import com.taobao.text.Decoration; 24 | import com.taobao.text.LineRenderer; 25 | import com.taobao.text.Renderer; 26 | import com.taobao.text.ui.LabelElement; 27 | import com.taobao.text.ui.RowElement; 28 | import com.taobao.text.ui.TableElement; 29 | 30 | import java.util.ArrayList; 31 | import java.util.Iterator; 32 | import java.util.LinkedHashSet; 33 | import java.util.Map; 34 | 35 | public class MapRenderer extends Renderer> { 36 | 37 | @Override 38 | public Class> getType() { 39 | Object mapClass = Map.class; 40 | return (Class>)mapClass; 41 | } 42 | 43 | @Override 44 | public LineRenderer renderer(Iterator> stream) { 45 | 46 | TableElement table = new TableElement(); 47 | LinkedHashSet current = new LinkedHashSet(); 48 | LinkedHashSet bilto = new LinkedHashSet(); 49 | 50 | ArrayList renderers = new ArrayList(); 51 | 52 | while (stream.hasNext()) { 53 | 54 | Map row = stream.next(); 55 | 56 | if (row.size() > 0) { 57 | 58 | bilto.clear(); 59 | for (Map.Entry entry : row.entrySet()) { 60 | bilto.add(String.valueOf(entry.getKey())); 61 | } 62 | 63 | // Create a new table if needed 64 | if (!current.equals(bilto)) { 65 | if (table.getRows().size() > 0) { 66 | renderers.add(table.renderer()); 67 | } 68 | table = new TableElement().rightCellPadding(1); 69 | RowElement header = new RowElement(true); 70 | header.style(Decoration.bold.fg(Color.black).bg(Color.white)); 71 | for (String s : bilto) { 72 | header.add(s); 73 | } 74 | table.add(header); 75 | current = bilto; 76 | } 77 | 78 | // 79 | RowElement r = new RowElement(); 80 | for (String s : bilto) { 81 | r.add(String.valueOf(row.get(s))); 82 | } 83 | table.add(r); 84 | } 85 | } 86 | 87 | // 88 | if (table.getRows().size() > 0) { 89 | renderers.add(table.renderer()); 90 | } 91 | 92 | // 93 | return LineRenderer.vertical(renderers); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/MemoryUsageLineRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.renderers; 21 | 22 | import java.lang.management.MemoryUsage; 23 | 24 | import com.taobao.text.Color; 25 | import com.taobao.text.LineReader; 26 | import com.taobao.text.LineRenderer; 27 | import com.taobao.text.RenderAppendable; 28 | 29 | class MemoryUsageLineRenderer extends LineRenderer { 30 | 31 | /** . */ 32 | private final MemoryUsage usage; 33 | 34 | MemoryUsageLineRenderer(MemoryUsage usage) { 35 | this.usage = usage; 36 | } 37 | 38 | @Override 39 | public int getActualWidth() { 40 | return 32; 41 | } 42 | 43 | @Override 44 | public int getMinWidth() { 45 | return 4; 46 | } 47 | 48 | @Override 49 | public int getMinHeight(int width) { 50 | return 1; 51 | } 52 | 53 | @Override 54 | public int getActualHeight(int width) { 55 | return 2; 56 | } 57 | 58 | @Override 59 | public LineReader reader(int width) { 60 | return reader(width, 2); 61 | } 62 | 63 | @Override 64 | public LineReader reader(final int width, final int height) { 65 | return new LineReader() { 66 | 67 | /** . */ 68 | private int index = 0; 69 | 70 | public boolean hasLine() { 71 | return index < height; 72 | } 73 | public void renderLine(RenderAppendable to) throws IllegalStateException { 74 | if (!hasLine()) { 75 | throw new IllegalStateException(); 76 | } 77 | long range = usage.getMax() - usage.getInit(); 78 | Color previous = null; 79 | 80 | if (usage.getMax() > 0) { 81 | long a = (width * usage.getUsed()) / (usage.getMax()); 82 | long b = (width * usage.getCommitted()) / (usage.getMax()); 83 | for (int i = 0;i < width;i++) { 84 | Color current; 85 | if (i >= b) { 86 | // MAX 87 | current = Color.green; 88 | } else if (i >= a) { 89 | // COMMITED 90 | current = Color.blue; 91 | } else { 92 | // USED 93 | current = Color.cyan; 94 | } 95 | if (previous != current) { 96 | if (previous != null) { 97 | to.leaveStyle(); 98 | } 99 | to.enterStyle(current.bg()); 100 | previous = current; 101 | } 102 | to.append(' '); 103 | } 104 | if (previous != null) { 105 | to.leaveStyle(); 106 | } 107 | } else { 108 | for (int i = 0;i < width;i++) { 109 | to.append(' '); 110 | } 111 | } 112 | index++; 113 | } 114 | }; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/MemoryUsageRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.renderers; 21 | 22 | import java.lang.management.MemoryUsage; 23 | import java.util.ArrayList; 24 | import java.util.Iterator; 25 | 26 | import com.taobao.text.LineRenderer; 27 | import com.taobao.text.Renderer; 28 | 29 | public class MemoryUsageRenderer extends Renderer { 30 | 31 | @Override 32 | public Class getType() { 33 | return MemoryUsage.class; 34 | } 35 | 36 | @Override 37 | public LineRenderer renderer(Iterator stream) { 38 | ArrayList renderers = new ArrayList(); 39 | while (stream.hasNext()) { 40 | MemoryUsage usage = stream.next(); 41 | renderers.add(new MemoryUsageLineRenderer(usage)); 42 | } 43 | return LineRenderer.vertical(renderers); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/ObjectNameRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | package com.taobao.text.renderers; 20 | 21 | import com.taobao.text.Color; 22 | import com.taobao.text.Decoration; 23 | import com.taobao.text.LineRenderer; 24 | import com.taobao.text.Renderer; 25 | import com.taobao.text.ui.LabelElement; 26 | import com.taobao.text.ui.Overflow; 27 | import com.taobao.text.ui.RowElement; 28 | import com.taobao.text.ui.TableElement; 29 | import com.taobao.text.util.Utils; 30 | 31 | import javax.management.InstanceNotFoundException; 32 | import javax.management.IntrospectionException; 33 | import javax.management.JMException; 34 | import javax.management.MBeanInfo; 35 | import javax.management.MBeanServer; 36 | import javax.management.ObjectName; 37 | import javax.management.ReflectionException; 38 | 39 | import java.lang.management.ManagementFactory; 40 | import java.util.Collections; 41 | import java.util.Iterator; 42 | import java.util.List; 43 | 44 | /** 45 | * @author Julien Viet 46 | */ 47 | public class ObjectNameRenderer extends Renderer { 48 | 49 | @Override 50 | public Class getType() { 51 | return ObjectName.class; 52 | } 53 | 54 | @Override 55 | public LineRenderer renderer(Iterator stream) { 56 | 57 | MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 58 | 59 | List names = Utils.list(stream); 60 | Collections.sort(names); 61 | 62 | // 63 | TableElement table = new TableElement().overflow(Overflow.HIDDEN).rightCellPadding(1); 64 | 65 | // Header 66 | table.add( 67 | new RowElement(). 68 | style(Decoration.bold.fg(Color.black).bg(Color.white)). 69 | add("NAME", "CLASSNAME", "MXBEAN", "DESCRIPTION") 70 | ); 71 | 72 | // 73 | for (ObjectName name : names) { 74 | 75 | String className; 76 | String description; 77 | String mxbean; 78 | try { 79 | MBeanInfo info = server.getMBeanInfo(name); 80 | className = info.getClassName(); 81 | description = info.getDescription(); 82 | Object mxbeanValue = info.getDescriptor().getFieldValue("mxbean"); 83 | mxbean = mxbeanValue != null ? mxbeanValue.toString() : "false"; 84 | } 85 | catch (JMException ignore) { 86 | className = ""; 87 | description = ""; 88 | mxbean = ""; 89 | } 90 | 91 | // 92 | table.row("" + name, className, mxbean, description); 93 | } 94 | 95 | // 96 | return table.renderer(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/renderers/ThreadRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.renderers; 21 | 22 | import com.taobao.text.Color; 23 | import com.taobao.text.Decoration; 24 | import com.taobao.text.LineRenderer; 25 | import com.taobao.text.Renderer; 26 | import com.taobao.text.Style; 27 | import com.taobao.text.ui.LabelElement; 28 | import com.taobao.text.ui.Overflow; 29 | import com.taobao.text.ui.RowElement; 30 | import com.taobao.text.ui.TableElement; 31 | import com.taobao.text.util.Utils; 32 | 33 | import java.lang.management.ManagementFactory; 34 | import java.lang.management.ThreadMXBean; 35 | import java.util.Collections; 36 | import java.util.Comparator; 37 | import java.util.EnumMap; 38 | import java.util.HashMap; 39 | import java.util.Iterator; 40 | import java.util.List; 41 | import java.util.Map; 42 | 43 | public class ThreadRenderer extends Renderer { 44 | 45 | /** . */ 46 | private static final EnumMap colorMapping = new EnumMap(Thread.State.class); 47 | 48 | static { 49 | colorMapping.put(Thread.State.NEW, Color.cyan); 50 | colorMapping.put(Thread.State.RUNNABLE, Color.green); 51 | colorMapping.put(Thread.State.BLOCKED, Color.red); 52 | colorMapping.put(Thread.State.WAITING, Color.yellow); 53 | colorMapping.put(Thread.State.TIMED_WAITING, Color.magenta); 54 | colorMapping.put(Thread.State.TERMINATED, Color.blue); 55 | } 56 | 57 | private long sampleInterval; 58 | 59 | public ThreadRenderer() { 60 | this.sampleInterval = 100; 61 | } 62 | 63 | public ThreadRenderer(long sampleInterval) { 64 | this.sampleInterval = sampleInterval; 65 | } 66 | 67 | @Override 68 | public Class getType() { 69 | return Thread.class; 70 | } 71 | 72 | @Override 73 | public LineRenderer renderer(Iterator stream) { 74 | 75 | // 76 | ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); 77 | 78 | // 79 | List threads = Utils.list(stream); 80 | 81 | // Sample CPU 82 | Map times1 = new HashMap(); 83 | for (Thread thread : threads) { 84 | long cpu = threadMXBean.getThreadCpuTime(thread.getId()); 85 | times1.put(thread.getId(), cpu); 86 | } 87 | 88 | try { 89 | Thread.sleep(sampleInterval); 90 | } 91 | catch (InterruptedException e) { 92 | Thread.currentThread().interrupt(); 93 | } 94 | 95 | // Resample 96 | Map times2 = new HashMap(threads.size()); 97 | for (Thread thread : threads) { 98 | long cpu = threadMXBean.getThreadCpuTime(thread.getId()); 99 | times2.put(thread.getId(), cpu); 100 | } 101 | 102 | // Compute delta map and total time 103 | long total = 0; 104 | Map deltas = new HashMap(threads.size()); 105 | for (Long id : times2.keySet()) { 106 | long time1 = times2.get(id); 107 | long time2 = times1.get(id); 108 | if (time1 == -1) { 109 | time1 = time2; 110 | } else if (time2 == -1) { 111 | time2 = time1; 112 | } 113 | long delta = time2 - time1; 114 | deltas.put(id, delta); 115 | total += delta; 116 | } 117 | 118 | // Compute cpu 119 | final HashMap cpus = new HashMap(threads.size()); 120 | for (Thread thread : threads) { 121 | long cpu = total == 0 ? 0 : Math.round((deltas.get(thread.getId()) * 100) / total); 122 | cpus.put(thread, cpu); 123 | } 124 | 125 | // Sort by CPU time : should be a rendering hint... 126 | Collections.sort(threads, new Comparator() { 127 | public int compare(Thread o1, Thread o2) { 128 | long l1 = cpus.get(o1); 129 | long l2 = cpus.get(o2); 130 | if (l1 < l2) { 131 | return 1; 132 | } else if (l1 > l2) { 133 | return -1; 134 | } else { 135 | return 0; 136 | } 137 | } 138 | }); 139 | 140 | // 141 | TableElement table = new TableElement(1,3,2,1,1,1,1,1,1).overflow(Overflow.HIDDEN).rightCellPadding(1); 142 | 143 | // Header 144 | table.add( 145 | new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add( 146 | "ID", 147 | "NAME", 148 | "GROUP", 149 | "PRIORITY", 150 | "STATE", 151 | "%CPU", 152 | "TIME", 153 | "INTERRUPTED", 154 | "DAEMON" 155 | ) 156 | ); 157 | 158 | // 159 | for (Thread thread : threads) { 160 | Color c = colorMapping.get(thread.getState()); 161 | long seconds = times2.get(thread.getId()) / 1000000000; 162 | long min = seconds / 60; 163 | String time = min + ":" + (seconds % 60); 164 | long cpu = cpus.get(thread); 165 | ThreadGroup group = thread.getThreadGroup(); 166 | 167 | LabelElement daemonLabel = new LabelElement(thread.isDaemon()); 168 | if (!thread.isDaemon()) { 169 | daemonLabel.setStyle(Style.style(Color.magenta)); 170 | } 171 | table.row( 172 | new LabelElement(thread.getId()), 173 | new LabelElement(thread.getName()), 174 | new LabelElement(group == null ? "" : group.getName()), 175 | new LabelElement(thread.getPriority()), 176 | new LabelElement(thread.getState()).style(c.fg()), 177 | new LabelElement(cpu), 178 | new LabelElement(time), 179 | new LabelElement(thread.isInterrupted()), 180 | daemonLabel 181 | ); 182 | } 183 | 184 | // 185 | return table.renderer(); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/stream/Consumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.stream; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * Defines the interface for a consumer. 26 | * 27 | * @param the consumed element generic type 28 | */ 29 | public interface Consumer { 30 | 31 | /** 32 | * Provide an element. 33 | * 34 | * @param element the provided element 35 | * @throws Exception any exception 36 | */ 37 | void provide(C element) throws Exception; 38 | 39 | /** 40 | * Flush the stream. 41 | * 42 | * @throws IOException any io exception 43 | */ 44 | void flush() throws IOException; 45 | 46 | /** 47 | * Returns the class of the element generic type. 48 | * 49 | * @return the consumed type 50 | */ 51 | Class getConsumedType(); 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/stream/Filter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.stream; 21 | 22 | /** 23 | * A filter is the combination of a producer and a consumer. 24 | * 25 | * @param the consumed element generic type 26 | * @param

the produced element generic type 27 | * @param the consumer generic type 28 | */ 29 | public interface Filter> extends Consumer, Producer { 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/stream/Producer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.stream; 21 | 22 | /** 23 | * A producer that produces elements in a specific consumer. 24 | * 25 | * @param

the produced element generic type 26 | * @param the consumer element generic type 27 | */ 28 | public interface Producer> { 29 | 30 | /** 31 | * Returns the class of the produced type. 32 | * 33 | * @return the produced type 34 | */ 35 | Class

getProducedType(); 36 | 37 | /** 38 | * Open the producer with the specified consumer. 39 | * 40 | * @param consumer the consumer 41 | * @throws Exception any exception 42 | */ 43 | void open(C consumer) throws Exception; 44 | 45 | /** 46 | * Close the producer. 47 | * 48 | * @throws Exception any exception 49 | */ 50 | void close() throws Exception; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/BorderStyle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | public enum BorderStyle { 23 | 24 | DASHED('-', '|', ' '), 25 | 26 | STAR('*', '*', '*'); 27 | 28 | /** . */ 29 | final char horizontal; 30 | 31 | /** . */ 32 | final char vertical; 33 | 34 | /** . */ 35 | final char corner; 36 | 37 | private BorderStyle(char horizontal, char vertical, char corner) { 38 | this.horizontal = horizontal; 39 | this.vertical = vertical; 40 | this.corner = corner; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/Element.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import com.taobao.text.LineReader; 23 | import com.taobao.text.LineRenderer; 24 | import com.taobao.text.RenderAppendable; 25 | import com.taobao.text.Style; 26 | 27 | public abstract class Element { 28 | 29 | /** . */ 30 | private Style.Composite style; 31 | 32 | protected Element() { 33 | this.style = null; 34 | } 35 | 36 | public abstract LineRenderer renderer(); 37 | 38 | public void render(RenderAppendable to) { 39 | LineRenderer renderer = renderer(); 40 | 41 | // For now height - 1 because of the char that goes to the line in some impl 42 | LineReader reader = renderer.reader(to.getWidth(), to.getHeight() - 1); 43 | if (reader != null) { 44 | while (reader.hasLine()) { 45 | reader.renderLine(to); 46 | to.append('\n'); 47 | } 48 | } 49 | } 50 | 51 | public final Style.Composite getStyle() { 52 | return style; 53 | } 54 | 55 | public final void setStyle(Style.Composite style) { 56 | this.style = style; 57 | } 58 | 59 | public Element style(Style.Composite style) { 60 | setStyle(style); 61 | return this; 62 | } 63 | 64 | public static RowElement row() { 65 | return new RowElement(); 66 | } 67 | 68 | public static RowElement header() { 69 | return new RowElement(true); 70 | } 71 | 72 | public static LabelElement label(String value) { 73 | return new LabelElement(value); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/ElementRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import java.util.Iterator; 23 | import java.util.LinkedList; 24 | 25 | import com.taobao.text.LineRenderer; 26 | import com.taobao.text.Renderer; 27 | 28 | public class ElementRenderer extends Renderer { 29 | 30 | @Override 31 | public Class getType() { 32 | return Element.class; 33 | } 34 | 35 | @Override 36 | public LineRenderer renderer(Iterator stream) { 37 | if (stream.hasNext()) { 38 | Element element = stream.next(); 39 | if (stream.hasNext()) { 40 | LinkedList renderers = new LinkedList(); 41 | renderers.add(element.renderer()); 42 | while (stream.hasNext()) { 43 | element = stream.next(); 44 | renderers.add(element.renderer()); 45 | } 46 | return LineRenderer.vertical(renderers); 47 | } else { 48 | return element.renderer(); 49 | } 50 | } else { 51 | throw new UnsupportedOperationException("todo"); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/LabelElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import com.taobao.text.LineRenderer; 23 | import com.taobao.text.Style; 24 | import com.taobao.text.util.CharSlicer; 25 | import com.taobao.text.util.Pair; 26 | import com.taobao.text.util.Utils; 27 | 28 | public class LabelElement extends Element { 29 | 30 | /** . */ 31 | final String value; 32 | 33 | /** . */ 34 | final int minWidth; 35 | 36 | /** . */ 37 | final int actualWidth; 38 | 39 | /** . */ 40 | final int actualHeight; 41 | 42 | /** . */ 43 | final CharSlicer slicer; 44 | 45 | /** 46 | * Create a new label element 47 | * 48 | * @param value the label value 49 | * @param minWidth the label minimum width 50 | * @throws IllegalArgumentException if the minimum width is negative 51 | */ 52 | public LabelElement(Object value, int minWidth) throws IllegalArgumentException { 53 | if (minWidth < 0) { 54 | throw new IllegalArgumentException("No negative min size allowed"); 55 | } 56 | 57 | //text-ui currently can not process \r, so replace to \n. @duanling 2016/4/1 58 | String s = String.valueOf(value); 59 | s = Utils.replace(s, "\\r\\n", "\n"); 60 | s = Utils.replace(s, "\\r", "\n"); 61 | 62 | // Determine size 63 | CharSlicer slicer = new CharSlicer(s); 64 | Pair size = slicer.size(); 65 | 66 | // 67 | this.value = s; 68 | this.minWidth = Math.min(size.getFirst(), minWidth); 69 | this.actualWidth = size.getFirst(); 70 | this.actualHeight = size.getSecond(); 71 | this.slicer = slicer; 72 | } 73 | 74 | public LabelElement(String value) { 75 | this((Object)value); 76 | } 77 | 78 | public LabelElement(String value, int minWidth) { 79 | this((Object)value, minWidth); 80 | } 81 | 82 | public LabelElement(Object value) { 83 | this(value, 1); 84 | } 85 | 86 | public String getValue() { 87 | return value; 88 | } 89 | 90 | public LineRenderer renderer() { 91 | return new LabelLineRenderer(this); 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return "Label[" + value + "]"; 97 | } 98 | 99 | @Override 100 | public LabelElement style(Style.Composite style) { 101 | return (LabelElement)super.style(style); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/LabelLineRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import com.taobao.text.LineReader; 23 | import com.taobao.text.LineRenderer; 24 | import com.taobao.text.util.Pair; 25 | 26 | class LabelLineRenderer extends LineRenderer { 27 | 28 | /** . */ 29 | private final LabelElement element; 30 | 31 | LabelLineRenderer(LabelElement element) { 32 | this.element = element; 33 | } 34 | 35 | @Override 36 | public int getMinWidth() { 37 | return element.minWidth; 38 | } 39 | 40 | @Override 41 | public int getActualWidth() { 42 | return element.actualWidth; 43 | } 44 | 45 | @Override 46 | public int getActualHeight(int width) { 47 | return element.slicer.lines(width).length; 48 | } 49 | 50 | @Override 51 | public int getMinHeight(int width) { 52 | // For now we don't support cropping 53 | return getActualHeight(width); 54 | } 55 | 56 | @Override 57 | public LineReader reader(int width) { 58 | return reader(width, -1); 59 | } 60 | 61 | @Override 62 | public LineReader reader(final int width, int height) { 63 | if (width == 0) { 64 | return null; 65 | } else { 66 | Pair[] lines = element.slicer.lines(width); 67 | if (height == -1) { 68 | height = lines.length; 69 | } 70 | if (lines.length > height) { 71 | return null; 72 | } else { 73 | return new LabelReader(element, lines, width, height); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/LabelReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import com.taobao.text.LineReader; 23 | import com.taobao.text.RenderAppendable; 24 | import com.taobao.text.Style; 25 | import com.taobao.text.util.BlankSequence; 26 | import com.taobao.text.util.Pair; 27 | 28 | class LabelReader implements LineReader { 29 | 30 | /** . */ 31 | private final LabelElement element; 32 | 33 | /** . */ 34 | private final Pair[] lines; 35 | 36 | /** . */ 37 | private final int width; 38 | 39 | /** . */ 40 | private final int height; 41 | 42 | LabelReader(LabelElement element, Pair[] lines, int width, int height) { 43 | this.element = element; 44 | this.lines = lines; 45 | this.height = height; 46 | this.width = width; 47 | } 48 | 49 | /** . */ 50 | private int index = 0; 51 | 52 | public boolean hasLine() { 53 | return index < height; 54 | } 55 | 56 | public void renderLine(RenderAppendable to) { 57 | if (index >= height) { 58 | throw new IllegalStateException(); 59 | } else { 60 | Style.Composite style = element.getStyle(); 61 | if (style != null) { 62 | to.enterStyle(style); 63 | } 64 | if (index < lines.length) { 65 | Pair a = lines[index]; 66 | to.append(element.value, a.getFirst(), a.getSecond()); 67 | int missing = width - (a.getSecond() - a.getFirst()); 68 | if (missing > 0) { 69 | to.append(BlankSequence.create(missing)); 70 | } 71 | } else { 72 | for (int i = 0;i < width;i++) { 73 | to.append(' '); 74 | } 75 | } 76 | index++; 77 | if (style != null) { 78 | to.leaveStyle(); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/Layout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import java.util.Arrays; 23 | 24 | /** 25 | * The layout computes the lengths of a list of contiguous cells. 26 | */ 27 | public abstract class Layout { 28 | 29 | public static Layout flow() { 30 | return RTL; 31 | } 32 | 33 | public static Layout weighted(int... weights) throws NullPointerException, IllegalArgumentException { 34 | return new Weighted(weights); 35 | } 36 | 37 | /** 38 | * Computes the list of lengths for the specifid list of cells with the following constraints: 39 | * 40 | *

    41 | *
  • the sum of the returned array elements must be equals to the totalLength argument
  • 42 | *
  • a cell length should never be lesser than the provided minimum length
  • 43 | *
44 | * 45 | * The returned array is the list of lengths from left to right, the array size may be less than the 46 | * number of cells (i.e the size of the actualLengths and minLengths arguments). Missing 47 | * cells are just be discarded and not part of the resulting layout. Array should contain only positive values, 48 | * any zero length cell should be discarded. When cells must be discarded it must begin with the tail of the 49 | * list, i.e it is not allowed to discard a cell that does not have a successor. 50 | * 51 | * 52 | * @param spaced true if the cells are separated by one char 53 | * @param totalLength the total length of the line 54 | * @param actualLengths the actual length : an estimation of what cell's desired length 55 | * @param minLengths the minmum length : the length under which a cell cannot be rendered 56 | * @return the list of length. 57 | */ 58 | abstract int[] compute(boolean spaced, int totalLength, int[] actualLengths, int[] minLengths); 59 | 60 | public static class Weighted extends Layout { 61 | 62 | /** The weights. */ 63 | private final int[] weights; 64 | 65 | /** 66 | * Create a new weighted layout. 67 | * 68 | * @param weights the weights 69 | * @throws NullPointerException if the weights argument is null 70 | * @throws IllegalArgumentException if any weight is negative 71 | */ 72 | private Weighted(int... weights) throws NullPointerException, IllegalArgumentException { 73 | if (weights == null) { 74 | throw new NullPointerException("No null weights accepted"); 75 | } 76 | for (int weight : weights) { 77 | if (weight < 0) { 78 | throw new IllegalArgumentException("No negative weight accepted"); 79 | } 80 | } 81 | this.weights = weights.clone(); 82 | } 83 | 84 | public int[] getWeights() { 85 | return weights.clone(); 86 | } 87 | 88 | @Override 89 | int[] compute(boolean spaced, int length, int[] actualLengths, int[] minLengths) { 90 | 91 | // 92 | int count = Math.min(actualLengths.length, weights.length); 93 | 94 | // 95 | for (int i = count;i > 0;i--) { 96 | 97 | // 98 | int totalLength = length; 99 | int totalWeight = 0; 100 | for (int j = 0;j < i;j++) { 101 | totalWeight += weights[j]; 102 | if (spaced) { 103 | if (j > 0) { 104 | totalLength--; 105 | } 106 | } 107 | } 108 | 109 | // Compute the length of each cell 110 | int[] ret = new int[i]; 111 | for (int j = 0;j < i;j++) { 112 | int w = totalLength * weights[j]; 113 | if (w < minLengths[j] * totalWeight) { 114 | ret = null; 115 | break; 116 | } else { 117 | ret[j] = w; 118 | } 119 | } 120 | 121 | // 122 | if (ret != null) { 123 | // Error based scaling inspired from Brensenham algorithm: 124 | // => sum of the weights == length 125 | // => minimize error 126 | // for instance with "foo","bar" scaled to 11 chars 127 | // using floor + division gives "foobar_____" 128 | // this methods gives "foo_bar____" 129 | int err = 0; 130 | for (int j = 0;j < ret.length;j++) { 131 | 132 | // Compute base value 133 | int value = ret[j] / totalWeight; 134 | 135 | // Lower value 136 | int lower = value * totalWeight; 137 | int errLower = err + ret[j] - lower; 138 | 139 | // Upper value 140 | int upper = lower + totalWeight; 141 | int errUpper = err + ret[j] - upper; 142 | 143 | // We choose between lower/upper according to the accumulated error 144 | // and we propagate the error 145 | if (Math.abs(errLower) < Math.abs(errUpper)) { 146 | ret[j] = value; 147 | err = errLower; 148 | } else { 149 | ret[j] = value + 1; 150 | err = errUpper; 151 | } 152 | } 153 | return ret; 154 | } 155 | } 156 | 157 | // 158 | return null; 159 | } 160 | } 161 | 162 | private static final Layout RTL = new Layout() { 163 | 164 | @Override 165 | int[] compute(boolean spaced, int length, int[] actualLengths, int[] minLengths) { 166 | 167 | // 168 | int[] ret = actualLengths.clone(); 169 | 170 | // 171 | int totalLength = 0; 172 | for (int i = 0;i < actualLengths.length;i++) { 173 | totalLength += actualLengths[i]; 174 | if (spaced && i > 0) { 175 | totalLength++; 176 | } 177 | } 178 | 179 | // 180 | int index = minLengths.length - 1; 181 | while (totalLength > length && index >= 0) { 182 | int delta = totalLength - length; 183 | int bar = actualLengths[index] - minLengths[index]; 184 | if (delta <= bar) { 185 | // We are done 186 | totalLength = length; 187 | ret[index] -= delta; 188 | } else { 189 | int foo = actualLengths[index]; 190 | if (spaced) { 191 | if (index > 0) { 192 | foo++; 193 | } 194 | } 195 | totalLength -= foo; 196 | ret[index] = 0; 197 | index--; 198 | } 199 | } 200 | 201 | // 202 | if (totalLength > 0) { 203 | if (index == minLengths.length - 1) { 204 | return ret; 205 | } else { 206 | return Arrays.copyOf(ret, index + 1); 207 | } 208 | } else { 209 | return null; 210 | } 211 | } 212 | }; 213 | /* 214 | 215 | public static final ColumnLayout DISTRIBUTED = new ColumnLayout() { 216 | @Override 217 | public int[] compute(Border border, int width, int[] widths, int[] minWidths) { 218 | int index = 0; 219 | while (true) { 220 | 221 | // Compute now the number of chars 222 | boolean done = false; 223 | int total = 0; 224 | for (int i = 0;i < widths.length;i++) { 225 | if (widths[i] >= minWidths[i]) { 226 | total += widths[i]; 227 | if (border != null) { 228 | if (done) { 229 | total++; 230 | } 231 | else { 232 | total += 2; 233 | done = true; 234 | } 235 | } 236 | } 237 | } 238 | 239 | // It's not valid 240 | if (total == 0) { 241 | return null; 242 | } 243 | 244 | // 245 | int delta = width - total; 246 | 247 | // 248 | if (delta == 0) { 249 | break; 250 | } else if (delta > 0) { 251 | switch (widths[index]) { 252 | case 0: 253 | break; 254 | default: 255 | widths[index]++; 256 | break; 257 | } 258 | index = (index + 1) % widths.length; 259 | } else { 260 | 261 | // First try to remove from a column above min size 262 | int found = -1; 263 | for (int i = widths.length - 1;i >= 0;i--) { 264 | int p = (index + i) % widths.length; 265 | if (widths[p] > minWidths[p]) { 266 | found = p; 267 | if (--index < 0) { 268 | index += widths.length; 269 | } 270 | break; 271 | } 272 | } 273 | 274 | // If we haven't found a victim then we consider removing a column 275 | if (found == -1) { 276 | for (int i = widths.length - 1;i >= 0;i--) { 277 | if (widths[i] > 0) { 278 | found = i; 279 | break; 280 | } 281 | } 282 | } 283 | 284 | // We couldn't find any solution we give up 285 | if (found == -1) { 286 | break; 287 | } else { 288 | widths[found]--; 289 | } 290 | } 291 | } 292 | 293 | // 294 | return widths; 295 | } 296 | }; 297 | */ 298 | } 299 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/Overflow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | /** 23 | * Overflow control. 24 | */ 25 | public enum Overflow { 26 | 27 | /** 28 | * Wrap. 29 | */ 30 | WRAP, 31 | 32 | /** 33 | * Hidden. 34 | */ 35 | HIDDEN 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/RowElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Collections; 24 | import java.util.List; 25 | 26 | import com.taobao.text.Style; 27 | 28 | public class RowElement extends Element { 29 | 30 | /** . */ 31 | List cols; 32 | 33 | /** . */ 34 | final boolean header; 35 | 36 | public RowElement() { 37 | this(false); 38 | } 39 | 40 | public RowElement(boolean header) { 41 | this.cols = new ArrayList(); 42 | this.header = header; 43 | } 44 | 45 | public int getSize() { 46 | return cols.size(); 47 | } 48 | 49 | public Element getCol(int index) { 50 | return cols.get(index); 51 | } 52 | 53 | public RowElement add(Element... cols) { 54 | Collections.addAll(this.cols, cols); 55 | return this; 56 | } 57 | 58 | public RowElement add(String... cols) { 59 | for (String col : cols) { 60 | this.cols.add(new LabelElement(col)); 61 | } 62 | return this; 63 | } 64 | 65 | @Override 66 | public RowElement style(Style.Composite style) { 67 | return (RowElement)super.style(style); 68 | } 69 | 70 | @Override 71 | public RowLineRenderer renderer() { 72 | return new RowLineRenderer(this, null, 0, 0); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/RowLineRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Arrays; 24 | import java.util.List; 25 | 26 | import com.taobao.text.LineReader; 27 | import com.taobao.text.LineRenderer; 28 | import com.taobao.text.RenderAppendable; 29 | import com.taobao.text.Style; 30 | 31 | class RowLineRenderer extends LineRenderer { 32 | 33 | /** . */ 34 | private final List cols; 35 | 36 | /** . */ 37 | private final Style.Composite style; 38 | 39 | /** . */ 40 | final int leftCellPadding; 41 | 42 | /** . */ 43 | final int rightCellPadding; 44 | 45 | /** . */ 46 | private final BorderStyle separator; 47 | 48 | RowLineRenderer(RowElement row, BorderStyle separator, int leftCellPadding, int rightCellPadding) { 49 | 50 | List cols = new ArrayList(row.cols.size()); 51 | for (Element col : row.cols) { 52 | cols.add(col.renderer()); 53 | } 54 | 55 | // 56 | this.cols = cols; 57 | this.style = row.getStyle(); 58 | this.separator = separator; 59 | this.leftCellPadding = leftCellPadding; 60 | this.rightCellPadding = rightCellPadding; 61 | } 62 | 63 | int getSize() { 64 | return cols.size(); 65 | } 66 | 67 | public List getCols() { 68 | return cols; 69 | } 70 | 71 | @Override 72 | public int getActualWidth() { 73 | int actualWidth = 0; 74 | for (int i = 0;i < cols.size();i++) { 75 | LineRenderer col = cols.get(i); 76 | actualWidth += col.getActualWidth(); 77 | actualWidth += leftCellPadding; 78 | actualWidth += rightCellPadding; 79 | if (separator != null && i > 0) { 80 | actualWidth++; 81 | } 82 | } 83 | return actualWidth; 84 | } 85 | 86 | @Override 87 | public int getMinWidth() { 88 | int minWidth = 0; 89 | for (int i = 0;i < cols.size();i++) { 90 | LineRenderer col = cols.get(i); 91 | minWidth += col.getMinWidth(); 92 | minWidth += leftCellPadding; 93 | minWidth += rightCellPadding; 94 | if (separator != null && i > 0) { 95 | minWidth++; 96 | } 97 | } 98 | return minWidth; 99 | } 100 | 101 | @Override 102 | public int getActualHeight(int width) { 103 | int actualHeight = 0; 104 | for (LineRenderer col : cols) { 105 | actualHeight = Math.max(actualHeight, col.getActualHeight(width)); 106 | } 107 | return actualHeight; 108 | } 109 | 110 | @Override 111 | public int getMinHeight(int width) { 112 | int minHeight = 0; 113 | for (LineRenderer col : cols) { 114 | minHeight = Math.max(minHeight, col.getMinHeight(width)); 115 | } 116 | return minHeight; 117 | } 118 | 119 | // todo look at : 120 | // if (i > 0) { 121 | // to.append(b.horizontal); 122 | // } 123 | // in relation to widths array that can contain (should?) 0 value 124 | LineReader renderer(final int[] widths, int height) { 125 | final LineReader[] readers = new LineReader[widths.length]; 126 | for (int i = 0;i < readers.length;i++) { 127 | LineRenderer renderer = cols.get(i); 128 | LineReader reader = renderer.reader(widths[i] - leftCellPadding - rightCellPadding, height); 129 | readers[i] = reader; 130 | } 131 | 132 | // 133 | return new LineReader() { 134 | 135 | /** . */ 136 | private boolean done = false; 137 | 138 | public boolean hasLine() { 139 | return !done; 140 | } 141 | 142 | public void renderLine(RenderAppendable to) { 143 | if (!hasLine()) { 144 | throw new IllegalStateException(); 145 | } 146 | 147 | // 148 | if (style != null) { 149 | to.enterStyle(style); 150 | } 151 | 152 | // 153 | for (int i = 0;i < readers.length;i++) { 154 | LineReader reader = readers[i]; 155 | 156 | // 157 | if (i > 0) { 158 | if (separator != null) { 159 | to.styleOff(); 160 | to.append(separator.vertical); 161 | to.styleOn(); 162 | } 163 | } 164 | if (reader != null && reader.hasLine()) { 165 | // Left padding 166 | if (leftCellPadding > 0) { 167 | for (int j = 0;j < leftCellPadding;j++) { 168 | to.append(' '); 169 | } 170 | } 171 | reader.renderLine(to); 172 | // Right padding 173 | if (rightCellPadding > 0) { 174 | for (int j = 0;j < rightCellPadding;j++) { 175 | to.append(' '); 176 | } 177 | } 178 | } else { 179 | readers[i] = null; 180 | for (int j = widths[i];j > 0;j--) { 181 | to.append(' '); 182 | } 183 | } 184 | } 185 | 186 | // 187 | if (style != null) { 188 | to.leaveStyle(); 189 | } 190 | 191 | 192 | // Update status 193 | done = true; 194 | for (LineReader reader : readers) { 195 | if (reader != null) { 196 | if (reader.hasLine()) { 197 | done = false; 198 | break; 199 | } 200 | } 201 | } 202 | } 203 | }; 204 | } 205 | 206 | @Override 207 | public LineReader reader(int width) { 208 | int[] widths = new int[cols.size()]; 209 | int[] minWidths = new int[cols.size()]; 210 | for (int i = 0;i < cols.size();i++) { 211 | LineRenderer renderable = cols.get(i); 212 | widths[i] = Math.max(widths[i], renderable.getActualWidth()); 213 | minWidths[i] = Math.max(minWidths[i], renderable.getMinWidth()); 214 | } 215 | widths = Layout.flow().compute(false, width, widths, minWidths); 216 | if (widths == null) { 217 | return null; 218 | } else { 219 | // Size could be smaller and lead to ArrayIndexOutOfBounds later 220 | widths = Arrays.copyOf(widths, minWidths.length); 221 | return renderer(widths, -1); 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/TableElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import com.taobao.text.LineRenderer; 26 | import com.taobao.text.Style; 27 | 28 | public class TableElement extends Element { 29 | 30 | /** . */ 31 | ArrayList rows = new ArrayList(); 32 | 33 | /** . */ 34 | protected BorderStyle border; 35 | 36 | /** . */ 37 | protected BorderStyle separator; 38 | 39 | /** . */ 40 | private Overflow overflow; 41 | 42 | /** The column layout. */ 43 | protected Layout columnLayout; 44 | 45 | /** The optional row row layout. */ 46 | protected Layout rowLayout; 47 | 48 | /** Cell padding left. */ 49 | private int leftCellPadding; 50 | 51 | /** Cell padding right. */ 52 | private int rightCellPadding; 53 | 54 | public TableElement() { 55 | this(Layout.flow(), Layout.flow()); 56 | } 57 | 58 | public TableElement(int ... columns) { 59 | this(Layout.flow(), Layout.weighted(columns)); 60 | } 61 | 62 | public TableElement(int[] rows, int[] columns) { 63 | this(Layout.weighted(rows), Layout.weighted(columns)); 64 | } 65 | 66 | private TableElement(Layout rowLayout, Layout columnLayout) { 67 | this.rowLayout = rowLayout; 68 | this.columnLayout = columnLayout; 69 | this.border = null; 70 | this.separator = null; 71 | this.overflow = Overflow.WRAP; 72 | this.leftCellPadding = 0; 73 | this.rightCellPadding = 0; 74 | } 75 | 76 | public TableElement add(RowElement row) { 77 | rows.add(row); 78 | return this; 79 | } 80 | 81 | public TableElement add(RowElement... rows) { 82 | for (RowElement row : rows) { 83 | add(row); 84 | } 85 | return this; 86 | } 87 | 88 | public TableElement header(Element... cols) { 89 | return row(true, cols); 90 | } 91 | 92 | public TableElement row(Element... cols) { 93 | return row(false, cols); 94 | } 95 | 96 | public TableElement row(String... cols) { 97 | return row(false, cols); 98 | } 99 | 100 | public TableElement row(boolean header, Element... cols) { 101 | return add(new RowElement(header).add(cols)); 102 | } 103 | 104 | public TableElement row(boolean header, String... cols) { 105 | return add(new RowElement(header).add(cols)); 106 | } 107 | 108 | public Layout getColumnLayout() { 109 | return columnLayout; 110 | } 111 | 112 | public void setColumnLayout(Layout columnLayout) { 113 | if (columnLayout == null) { 114 | throw new NullPointerException("Column layout cannot be null"); 115 | } 116 | this.columnLayout = columnLayout; 117 | } 118 | 119 | public Layout getRowLayout() { 120 | return rowLayout; 121 | } 122 | 123 | public void setRowLayout(Layout rowLayout) { 124 | if (rowLayout == null) { 125 | throw new NullPointerException("Row layout cannot be null"); 126 | } 127 | this.rowLayout = rowLayout; 128 | } 129 | 130 | public LineRenderer renderer() { 131 | return new TableLineRenderer(this); 132 | } 133 | 134 | public TableElement withColumnLayout(Layout columnLayout) { 135 | setColumnLayout(columnLayout); 136 | return this; 137 | } 138 | 139 | public TableElement withRowLayout(Layout rowLayout) { 140 | setRowLayout(rowLayout); 141 | return this; 142 | } 143 | 144 | public List getRows() { 145 | return rows; 146 | } 147 | 148 | public BorderStyle getBorder() { 149 | return border; 150 | } 151 | 152 | public void setBorder(BorderStyle border) { 153 | this.border = border; 154 | } 155 | 156 | public TableElement border(BorderStyle border) { 157 | setBorder(border); 158 | return this; 159 | } 160 | 161 | public BorderStyle getSeparator() { 162 | return separator; 163 | } 164 | 165 | public void setSeparator(BorderStyle separator) { 166 | this.separator = separator; 167 | } 168 | 169 | public TableElement collapse() { 170 | setSeparator(null); 171 | return this; 172 | } 173 | 174 | public TableElement separator(BorderStyle separator) { 175 | setSeparator(separator); 176 | return this; 177 | } 178 | 179 | public void setOverflow(Overflow overflow) { 180 | this.overflow = overflow; 181 | } 182 | 183 | public final Overflow getOverflow() { 184 | return overflow; 185 | } 186 | 187 | public TableElement overflow(Overflow overflow) { 188 | setOverflow(overflow); 189 | return this; 190 | } 191 | 192 | public int getLeftCellPadding() { 193 | return leftCellPadding; 194 | } 195 | 196 | public void setLeftCellPadding(int leftCellPadding) { 197 | if (leftCellPadding < 0) { 198 | throw new IllegalArgumentException("No negative cell padding left accepted"); 199 | } 200 | this.leftCellPadding = leftCellPadding; 201 | } 202 | 203 | public TableElement leftCellPadding(int leftCellPadding) { 204 | setLeftCellPadding(leftCellPadding); 205 | return this; 206 | } 207 | 208 | public int getRightCellPadding() { 209 | return rightCellPadding; 210 | } 211 | 212 | public void setRightCellPadding(int rightCellPadding) { 213 | if (rightCellPadding < 0) { 214 | throw new IllegalArgumentException("No negative cell padding right accepted"); 215 | } 216 | this.rightCellPadding = rightCellPadding; 217 | } 218 | 219 | public TableElement rightCellPadding(int rightCellPadding) { 220 | setRightCellPadding(rightCellPadding); 221 | return this; 222 | } 223 | 224 | @Override 225 | public TableElement style(Style.Composite style) { 226 | return (TableElement)super.style(style); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/TableRowLineRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import java.util.List; 23 | 24 | import com.taobao.text.LineRenderer; 25 | 26 | class TableRowLineRenderer { 27 | 28 | /** . */ 29 | final TableLineRenderer table; 30 | 31 | /** . */ 32 | final RowLineRenderer row; 33 | 34 | /** . */ 35 | final boolean header; 36 | 37 | /** . */ 38 | private TableRowLineRenderer previous; 39 | 40 | /** . */ 41 | private TableRowLineRenderer next; 42 | 43 | /** . */ 44 | private int index; 45 | 46 | TableRowLineRenderer(TableLineRenderer table, RowElement row) { 47 | this.table = table; 48 | this.row = new RowLineRenderer(row, table.separator, table.leftCellPadding, table.rightCellPadding); 49 | this.header = row.header; 50 | this.index = 0; 51 | } 52 | 53 | TableRowLineRenderer add(TableRowLineRenderer next) { 54 | next.previous = this; 55 | next.index = index + 1; 56 | this.next = next; 57 | return next; 58 | } 59 | 60 | boolean hasTop() { 61 | return header && previous != null; 62 | } 63 | 64 | boolean hasBottom() { 65 | return header && next != null && !next.header; 66 | } 67 | 68 | int getIndex() { 69 | return index; 70 | } 71 | 72 | int getSize() { 73 | return index + 1; 74 | } 75 | 76 | TableRowLineRenderer previous() { 77 | return previous; 78 | } 79 | 80 | TableRowLineRenderer next() { 81 | return next; 82 | } 83 | 84 | boolean isHeader() { 85 | return header; 86 | } 87 | 88 | int getColsSize() { 89 | return row.getSize(); 90 | } 91 | 92 | List getCols() { 93 | return row.getCols(); 94 | } 95 | 96 | int getActualWidth() { 97 | return row.getActualWidth(); 98 | } 99 | 100 | int getMinWidth() { 101 | return row.getMinWidth(); 102 | } 103 | 104 | int getActualHeight(int width) { 105 | int actualHeight; 106 | switch (table.overflow) { 107 | case HIDDEN: 108 | actualHeight = 1; 109 | break; 110 | case WRAP: 111 | actualHeight = row.getActualHeight(width); 112 | break; 113 | default: 114 | throw new AssertionError(); 115 | } 116 | if (hasTop()) { 117 | actualHeight++; 118 | } 119 | if (hasBottom()) { 120 | actualHeight++; 121 | } 122 | return actualHeight; 123 | } 124 | 125 | int getActualHeight(int[] widths) { 126 | int actualHeight; 127 | switch (table.overflow) { 128 | case HIDDEN: 129 | actualHeight = 1; 130 | break; 131 | case WRAP: 132 | actualHeight = 0; 133 | for (int i = 0;i < widths.length;i++) { 134 | LineRenderer col = row.getCols().get(i); 135 | actualHeight = Math.max(actualHeight, col.getActualHeight(widths[i])); 136 | } 137 | break; 138 | default: 139 | throw new AssertionError(); 140 | } 141 | if (hasTop()) { 142 | actualHeight++; 143 | } 144 | if (hasBottom()) { 145 | actualHeight++; 146 | } 147 | return actualHeight; 148 | } 149 | 150 | int getMinHeight(int[] widths) { 151 | int minHeight; 152 | switch (table.overflow) { 153 | case HIDDEN: 154 | minHeight = 1; 155 | break; 156 | case WRAP: 157 | minHeight = 0; 158 | for (int i = 0;i < widths.length;i++) { 159 | LineRenderer col = row.getCols().get(i); 160 | minHeight = Math.max(minHeight, col.getMinHeight(widths[i])); 161 | } 162 | break; 163 | default: 164 | throw new AssertionError(); 165 | } 166 | if (hasTop()) { 167 | minHeight++; 168 | } 169 | if (hasBottom()) { 170 | minHeight++; 171 | } 172 | return minHeight; 173 | } 174 | 175 | TableRowReader renderer(int[] widths, int height) { 176 | return new TableRowReader(this, row, widths, height); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/TableRowReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import com.taobao.text.LineReader; 23 | import com.taobao.text.RenderAppendable; 24 | 25 | class TableRowReader implements LineReader { 26 | 27 | /** . */ 28 | private final TableRowLineRenderer renderer; 29 | 30 | /** . */ 31 | private final int[] widths; 32 | 33 | /** . */ 34 | private final RowLineRenderer row; 35 | 36 | /** . */ 37 | private LineReader reader; 38 | 39 | /** . */ 40 | private TableRowReader previous; 41 | 42 | /** . */ 43 | private TableRowReader next; 44 | 45 | /** . */ 46 | private BorderStyle top; 47 | 48 | /** . */ 49 | private BorderStyle bottom; 50 | 51 | /** . */ 52 | private final int height; 53 | 54 | /** 55 | * 0 -> render top 56 | * 1 -> render cells 57 | * 2 -> render bottom 58 | * 3 -> done 59 | */ 60 | private int status; 61 | 62 | TableRowReader(TableRowLineRenderer renderer, RowLineRenderer row, int[] widths, int height) { 63 | 64 | // 65 | this.renderer = renderer; 66 | this.row = row; 67 | this.widths = widths; 68 | this.reader = null; 69 | this.top = null; 70 | this.bottom = null; 71 | this.height = height; 72 | this.status = 1; 73 | } 74 | 75 | TableRowReader add(TableRowReader next) { 76 | next.previous = this; 77 | this.next = next; 78 | bottom = renderer.header ? (renderer.table.separator != null ? renderer.table.separator : BorderStyle.DASHED) : null; 79 | next.top = next.renderer.header && !renderer.header ? (next.renderer.table.separator != null ? next.renderer.table.separator : BorderStyle.DASHED) : null; 80 | next.status = next.top != null ? 0 : 1; 81 | return next; 82 | } 83 | 84 | TableRowReader previous() { 85 | return previous; 86 | } 87 | 88 | TableRowReader next() { 89 | return next; 90 | } 91 | 92 | boolean hasTop() { 93 | return renderer.header && previous != null; 94 | } 95 | 96 | boolean hasBottom() { 97 | return renderer.header && next != null && !next.renderer.header; 98 | } 99 | 100 | boolean isSeparator() { 101 | return status == 0 || status == 2; 102 | } 103 | 104 | public boolean hasLine() { 105 | return 0 <= status && status <= 2; 106 | } 107 | 108 | public void renderLine(RenderAppendable to) throws IllegalStateException { 109 | if (!hasLine()) { 110 | throw new IllegalStateException(); 111 | } 112 | switch (status) { 113 | case 0: 114 | case 2: { 115 | BorderStyle b = status == 0 ? top : bottom; 116 | to.styleOff(); 117 | for (int i = 0;i < widths.length;i++) { 118 | if (i > 0 && renderer.table.separator != null) { 119 | to.append(b.horizontal); 120 | } 121 | for (int j = 0;j < widths[i];j++) { 122 | to.append(b.horizontal); 123 | } 124 | } 125 | to.styleOn(); 126 | status++; 127 | break; 128 | } 129 | case 1: { 130 | 131 | // 132 | if (reader == null) { 133 | if (height > 0 && renderer.table.overflow == Overflow.WRAP) { 134 | int h = height; 135 | if (hasTop()) { 136 | h--; 137 | } 138 | if (hasBottom()) { 139 | h--; 140 | } 141 | reader = row.renderer(widths, h); 142 | } else { 143 | reader = row.renderer(widths, -1); 144 | } 145 | } 146 | 147 | // 148 | reader.renderLine(to); 149 | 150 | // 151 | if (renderer.table.overflow == Overflow.HIDDEN) { 152 | status = bottom != null ? 2 : 3; 153 | } else { 154 | if (!reader.hasLine()) { 155 | status = bottom != null ? 2 : 3; 156 | } 157 | } 158 | 159 | // 160 | break; 161 | } 162 | default: 163 | throw new AssertionError(); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/TextElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import com.taobao.text.LineRenderer; 23 | import com.taobao.text.Style; 24 | import com.taobao.text.util.Utils; 25 | 26 | import java.util.Iterator; 27 | 28 | public class TextElement extends Element { 29 | 30 | /** . */ 31 | final Iterable stream; 32 | 33 | /** . */ 34 | final int minWidth; 35 | 36 | /** . */ 37 | final int width; 38 | 39 | private static int width(int width, Iterator stream, CharSequence current, Integer from) { 40 | while (current == null) { 41 | if (stream.hasNext()) { 42 | Object next = stream.next(); 43 | if (next instanceof CharSequence) { 44 | current = (CharSequence)next; 45 | from = 0; 46 | } 47 | } else { 48 | break; 49 | } 50 | } 51 | if (current == null) { 52 | return width; 53 | } else { 54 | int pos = Utils.indexOf(current, from, '\n'); 55 | if (pos == -1) { 56 | return width(width + current.length() - from, stream, current, from); 57 | } else { 58 | return Math.max(width + pos - from, width(0, stream, null, 0)); 59 | } 60 | } 61 | } 62 | 63 | public TextElement(Iterable stream, int minWidth) { 64 | if (minWidth < 0) { 65 | throw new IllegalArgumentException("No negative min size allowed"); 66 | } 67 | 68 | // Determine width 69 | int width = width(0, stream.iterator(), null, null); 70 | 71 | // 72 | this.minWidth = Math.min(width, minWidth); 73 | this.stream = stream; 74 | this.width = width; 75 | } 76 | 77 | public TextElement(Iterable stream) { 78 | this(stream, 1); 79 | } 80 | 81 | public LineRenderer renderer() { 82 | // return new LabelRenderer(this); 83 | throw new UnsupportedOperationException(); 84 | } 85 | 86 | @Override 87 | public String toString() { 88 | return "TextElement[]"; 89 | } 90 | 91 | @Override 92 | public TextElement style(Style.Composite style) { 93 | return (TextElement)super.style(style); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/TreeElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import com.taobao.text.LineRenderer; 26 | 27 | public class TreeElement extends Element { 28 | 29 | /** An optional value element. */ 30 | Element value; 31 | 32 | /** . */ 33 | final List children = new ArrayList(); 34 | 35 | public TreeElement() { 36 | this((Element)null); 37 | } 38 | 39 | public TreeElement(Element value) { 40 | this.value = value; 41 | } 42 | 43 | public TreeElement(String value) { 44 | this.value = new LabelElement(value); 45 | } 46 | 47 | public TreeElement addChild(Element child) { 48 | children.add(child); 49 | return this; 50 | } 51 | 52 | public int getSize() { 53 | return children.size(); 54 | } 55 | 56 | public Element getValue() { 57 | return value; 58 | } 59 | 60 | public Element getNode(int index) { 61 | return children.get(index); 62 | } 63 | 64 | @Override 65 | public LineRenderer renderer() { 66 | return new TreeLineRenderer(this); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/TreeLineRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Iterator; 24 | import java.util.LinkedList; 25 | import java.util.List; 26 | 27 | import com.taobao.text.LineReader; 28 | import com.taobao.text.LineRenderer; 29 | import com.taobao.text.RenderAppendable; 30 | 31 | class TreeLineRenderer extends LineRenderer { 32 | 33 | /** . */ 34 | private final LineRenderer value; 35 | 36 | /** . */ 37 | private final List children; 38 | 39 | TreeLineRenderer(TreeElement tree) { 40 | 41 | ArrayList children = new ArrayList(tree.children.size()); 42 | for (Element child : tree.children) { 43 | children.add(child.renderer()); 44 | } 45 | 46 | // 47 | this.children = children; 48 | this.value = tree.value != null ? tree.value.renderer() : null; 49 | } 50 | 51 | @Override 52 | public int getActualWidth() { 53 | int width = value != null ? value.getActualWidth() : 0; 54 | for (LineRenderer child : children) { 55 | width = Math.max(width, 2 + child.getActualWidth()); 56 | } 57 | return width; 58 | } 59 | 60 | @Override 61 | public int getMinWidth() { 62 | int width = value != null ? value.getMinWidth() : 0; 63 | for (LineRenderer child : children) { 64 | width = Math.max(width, 2 + child.getMinWidth()); 65 | } 66 | return width; 67 | } 68 | 69 | @Override 70 | public int getActualHeight(int width) { 71 | throw new UnsupportedOperationException("Implement me"); 72 | } 73 | 74 | @Override 75 | public int getMinHeight(int width) { 76 | throw new UnsupportedOperationException("Implement me"); 77 | } 78 | 79 | @Override 80 | public LineReader reader(final int width) { 81 | 82 | 83 | final LinkedList readers = new LinkedList(); 84 | for (LineRenderer child : children) { 85 | readers.addLast(child.reader(width - 2)); 86 | } 87 | 88 | // 89 | return new LineReader() { 90 | 91 | /** . */ 92 | LineReader value = TreeLineRenderer.this.value != null ? TreeLineRenderer.this.value.reader(width) : null; 93 | 94 | /** . */ 95 | boolean node = true; 96 | 97 | public boolean hasLine() { 98 | if (value != null) { 99 | if (value.hasLine()) { 100 | return true; 101 | } else { 102 | value = null; 103 | } 104 | } 105 | while (readers.size() > 0) { 106 | if (readers.peekFirst().hasLine()) { 107 | return true; 108 | } else { 109 | readers.removeFirst(); 110 | node = true; 111 | } 112 | } 113 | return false; 114 | } 115 | 116 | public void renderLine(RenderAppendable to) { 117 | if (value != null) { 118 | if (value.hasLine()) { 119 | value.renderLine(to); 120 | } else { 121 | value = null; 122 | } 123 | } 124 | if (value == null) { 125 | while (readers.size() > 0) { 126 | LineReader first = readers.peekFirst(); 127 | if (first.hasLine()) { 128 | if (node) { 129 | to.append("+-"); 130 | node = false; 131 | } else { 132 | Iterator i = readers.descendingIterator(); 133 | boolean rest = false; 134 | while (i.hasNext()) { 135 | LineReader renderer = i.next(); 136 | if (i.hasNext()) { 137 | if (renderer.hasLine()) { 138 | rest = true; 139 | break; 140 | } 141 | } 142 | } 143 | if (rest) { 144 | to.append("| "); 145 | } else { 146 | to.append(" "); 147 | } 148 | } 149 | first.renderLine(to); 150 | break; 151 | } 152 | } 153 | } 154 | } 155 | }; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/UIBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import groovy.lang.Closure; 23 | import groovy.util.BuilderSupport; 24 | import org.codehaus.groovy.runtime.InvokerHelper; 25 | 26 | import com.taobao.text.Color; 27 | import com.taobao.text.LineRenderer; 28 | import com.taobao.text.Style; 29 | import com.taobao.text.util.Utils; 30 | 31 | import java.util.ArrayList; 32 | import java.util.Collections; 33 | import java.util.Iterator; 34 | import java.util.List; 35 | import java.util.Map; 36 | 37 | public class UIBuilder extends BuilderSupport implements Iterable { 38 | 39 | /** . */ 40 | private final List elements; 41 | 42 | public UIBuilder() { 43 | this.elements = new ArrayList(); 44 | } 45 | 46 | public List getElements() { 47 | return elements; 48 | } 49 | 50 | @Override 51 | protected Object doInvokeMethod(String methodName, Object name, Object args) { 52 | // if ("eval".equals(name)) { 53 | // List list = InvokerHelper.asList(args); 54 | // if (list.size() == 1 && list.get(0) instanceof Closure) { 55 | // EvalElement element = (EvalElement)super.doInvokeMethod(methodName, name, null); 56 | // element.closure = (Closure)list.get(0); 57 | // return element; 58 | // } else { 59 | // return super.doInvokeMethod(methodName, name, args); 60 | // } 61 | // } else { 62 | // return super.doInvokeMethod(methodName, name, args); 63 | // } 64 | return null; 65 | } 66 | 67 | @Override 68 | protected Object createNode(Object name) { 69 | return createNode(name, (Object)null); 70 | } 71 | 72 | @Override 73 | protected Object createNode(Object name, Map attributes, Object value) { 74 | Element element; 75 | if ("node".equals(name)) { 76 | if (value == null) { 77 | element = new TreeElement(); 78 | } else { 79 | element = new TreeElement(new LabelElement(value)); 80 | } 81 | } else if ("label".equals(name)) { 82 | element = new LabelElement(value); 83 | } else if ("table".equals(name)) { 84 | element = new TableElement(); 85 | } else if ("row".equals(name)) { 86 | element = new RowElement(); 87 | } else if ("header".equals(name)) { 88 | element = new RowElement(true); 89 | } 90 | // else if ("eval".equals(name)) { 91 | // element = new EvalElement(); 92 | // } 93 | else { 94 | throw new UnsupportedOperationException("Cannot build object with name " + name + " and value " + value); 95 | } 96 | 97 | // 98 | Style.Composite style = element.getStyle(); 99 | if (style == null) { 100 | style = Style.style(); 101 | } 102 | style = style. 103 | bold((Boolean)attributes.get("bold")). 104 | underline((Boolean)attributes.get("underline")). 105 | blink((Boolean)attributes.get("blink")); 106 | if (attributes.containsKey("fg")) { 107 | style = style.foreground((Color)attributes.get("fg")); 108 | } 109 | if (attributes.containsKey("foreground")) { 110 | style = style.foreground((Color)attributes.get("foreground")); 111 | } 112 | if (attributes.containsKey("bg")) { 113 | style = style.background((Color)attributes.get("bg")); 114 | } 115 | if (attributes.containsKey("background")) { 116 | style = style.background((Color)attributes.get("background")); 117 | } 118 | element.setStyle(style); 119 | 120 | // 121 | if (element instanceof TableElement) { 122 | TableElement table = (TableElement)element; 123 | 124 | // Columns 125 | Object columns = attributes.get("columns"); 126 | if (columns instanceof Iterable) { 127 | List list = Utils.list((Iterable)columns); 128 | int[] weights = new int[list.size()]; 129 | for (int i = 0;i < weights.length;i++) { 130 | weights[i] = list.get(i); 131 | } 132 | table.withColumnLayout(Layout.weighted(weights)); 133 | } 134 | 135 | // Columns 136 | Object rows = attributes.get("rows"); 137 | if (rows instanceof Iterable) { 138 | List list = Utils.list((Iterable)rows); 139 | int[] weights = new int[list.size()]; 140 | for (int i = 0;i < weights.length;i++) { 141 | weights[i] = list.get(i); 142 | } 143 | table.withRowLayout(Layout.weighted(weights)); 144 | } 145 | 146 | // Border 147 | Object borderAttr = attributes.get("border"); 148 | BorderStyle border; 149 | if (borderAttr instanceof Boolean && (Boolean)borderAttr) { 150 | border = BorderStyle.DASHED; 151 | } else if (borderAttr instanceof BorderStyle) { 152 | border = (BorderStyle)borderAttr; 153 | } else { 154 | border = null; 155 | } 156 | table.border(border); 157 | 158 | // Separator 159 | Object separatorAttr = attributes.get("separator"); 160 | BorderStyle separator; 161 | if (separatorAttr instanceof Boolean && (Boolean)separatorAttr) { 162 | separator = BorderStyle.DASHED; 163 | } else if (separatorAttr instanceof BorderStyle) { 164 | separator = (BorderStyle)separatorAttr; 165 | } else { 166 | separator = null; 167 | } 168 | table.separator(separator); 169 | 170 | // Overflow 171 | Object overflowAttr = attributes.get("overflow"); 172 | Overflow overflow; 173 | if ("hidden".equals(overflowAttr)) { 174 | overflow = Overflow.HIDDEN; 175 | } else if ("wrap".equals(overflowAttr)) { 176 | overflow = Overflow.WRAP; 177 | } else if (overflowAttr instanceof Overflow) { 178 | overflow = (Overflow)separatorAttr; 179 | } else { 180 | overflow = Overflow.WRAP; 181 | } 182 | table.overflow(overflow); 183 | 184 | // Cell left padding 185 | Object leftCellPaddingAttr = attributes.get("leftCellPadding"); 186 | int leftCellPadding = 0; 187 | if (leftCellPaddingAttr instanceof Number) { 188 | leftCellPadding = ((Number)leftCellPaddingAttr).intValue(); 189 | } 190 | table.setLeftCellPadding(leftCellPadding); 191 | 192 | // Cell right padding 193 | Object rightCellPaddingAttr = attributes.get("rightCellPadding"); 194 | int rightCellPadding = 0; 195 | if (rightCellPaddingAttr instanceof Number) { 196 | rightCellPadding = ((Number)rightCellPaddingAttr).intValue(); 197 | } 198 | table.setRightCellPadding(rightCellPadding); 199 | } 200 | 201 | // 202 | return element; 203 | } 204 | 205 | @Override 206 | protected Object createNode(Object name, Object value) { 207 | return createNode(name, Collections.emptyMap(), value); 208 | } 209 | 210 | @Override 211 | protected Object createNode(Object name, Map attributes) { 212 | return createNode(name, attributes, null); 213 | } 214 | 215 | @Override 216 | protected void setParent(Object parent, Object child) { 217 | if (parent instanceof TreeElement) { 218 | TreeElement parentElement = (TreeElement)parent; 219 | Element childElement = (Element)child; 220 | parentElement.addChild(childElement); 221 | } else if (parent instanceof TableElement) { 222 | TableElement parentElement = (TableElement)parent; 223 | RowElement childElement = (RowElement)child; 224 | parentElement.add(childElement); 225 | } else if (parent instanceof RowElement) { 226 | RowElement parentElement = (RowElement)parent; 227 | Element childElement = (Element)child; 228 | if (child instanceof TreeElement) { 229 | throw new IllegalArgumentException("A table cannot contain a tree element"); 230 | } 231 | parentElement.add(childElement); 232 | } else { 233 | throw new UnsupportedOperationException("Unrecognized parent " + parent); 234 | } 235 | } 236 | 237 | @Override 238 | protected void nodeCompleted(Object parent, Object child) { 239 | if (parent == null) { 240 | elements.add((Element)child); 241 | } 242 | super.nodeCompleted(parent, child); 243 | } 244 | 245 | public Iterator iterator() { 246 | return new Iterator() { 247 | Iterator i = elements.iterator(); 248 | public boolean hasNext() { 249 | return i.hasNext(); 250 | } 251 | public LineRenderer next() { 252 | return i.next().renderer(); 253 | } 254 | public void remove() { 255 | throw new UnsupportedOperationException(); 256 | } 257 | }; 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/ui/UIBuilderRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.ui; 21 | 22 | import java.util.Iterator; 23 | import java.util.LinkedList; 24 | 25 | import com.taobao.text.LineRenderer; 26 | import com.taobao.text.Renderer; 27 | 28 | public class UIBuilderRenderer extends Renderer { 29 | 30 | @Override 31 | public Class getType() { 32 | return UIBuilder.class; 33 | } 34 | 35 | @Override 36 | public LineRenderer renderer(Iterator stream) { 37 | LinkedList renderers = new LinkedList(); 38 | while (stream.hasNext()) { 39 | for (Element element : stream.next().getElements()) { 40 | renderers.add(element.renderer()); 41 | } 42 | } 43 | return LineRenderer.vertical(renderers); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/util/BaseIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.util; 21 | 22 | import java.util.Iterator; 23 | import java.util.NoSuchElementException; 24 | 25 | public class BaseIterator implements Iterator { 26 | 27 | public boolean hasNext() { 28 | return false; 29 | } 30 | 31 | public E next() { 32 | throw new NoSuchElementException("No more elements"); 33 | } 34 | 35 | public void remove() { 36 | throw new UnsupportedOperationException(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/util/BlankSequence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.util; 21 | 22 | import java.io.Serializable; 23 | 24 | /** 25 | * An immutable sequence of white spaces. 26 | */ 27 | public class BlankSequence implements CharSequence, Serializable { 28 | 29 | /** . */ 30 | private static final BlankSequence[] CACHE = new BlankSequence[64]; 31 | 32 | static { 33 | for (int i = 0;i < CACHE.length;i++) { 34 | CACHE[i] = new BlankSequence(i); 35 | } 36 | } 37 | 38 | public static BlankSequence create(int length) { 39 | if (length < 0) { 40 | throw new IllegalArgumentException("No negative length accepted"); 41 | } 42 | if (length < CACHE.length) { 43 | return CACHE[length]; 44 | } else { 45 | return new BlankSequence(length); 46 | } 47 | } 48 | 49 | /** . */ 50 | private final int length; 51 | 52 | /** . */ 53 | private String value; 54 | 55 | /** 56 | * Build a new blank sequence. 57 | * 58 | * @param length the length 59 | * @throws IllegalArgumentException when length is negative 60 | */ 61 | private BlankSequence(int length) throws IllegalArgumentException { 62 | if (length < 0) { 63 | throw new IllegalArgumentException(); 64 | } 65 | 66 | // 67 | this.length = length; 68 | this.value = null; 69 | } 70 | 71 | public int length() { 72 | return length; 73 | } 74 | 75 | public char charAt(int index) { 76 | checkIndex("index", index); 77 | return ' '; 78 | } 79 | 80 | public CharSequence subSequence(int start, int end) { 81 | checkIndex("start", start); 82 | checkIndex("end", end); 83 | if (start > end) { 84 | throw new IndexOutOfBoundsException("Start " + start + " cannot greater than end " + end); 85 | } 86 | return new BlankSequence(end - start); 87 | } 88 | 89 | @Override 90 | public String toString() { 91 | if (value == null) { 92 | if (length == 0) { 93 | value = ""; 94 | } else { 95 | char[] chars = new char[length]; 96 | for (int i = 0;i < length;i++) { 97 | chars[i] = ' '; 98 | } 99 | value = new String(chars, 0, chars.length); 100 | } 101 | } 102 | return value; 103 | } 104 | 105 | private void checkIndex(String name, int index) { 106 | if (index < 0) { 107 | throw new IndexOutOfBoundsException("No negative " + name + " value " + index); 108 | } 109 | if (index > length) { 110 | throw new IndexOutOfBoundsException("The " + name + " value " + index + " cannot greater than length " + length); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/util/CharSlicer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.util; 21 | 22 | import java.util.Iterator; 23 | import java.util.NoSuchElementException; 24 | 25 | public class CharSlicer { 26 | 27 | /** . */ 28 | private final String value; 29 | 30 | /** . */ 31 | private Pair size; 32 | 33 | public CharSlicer(String value) { 34 | this.value = value; 35 | this.size = size(); 36 | } 37 | 38 | public Pair size() { 39 | if (size == null) { 40 | size = size(value); 41 | } 42 | return size; 43 | } 44 | 45 | private static Pair size(String s) { 46 | int height = 1; 47 | int maxWidth = 0; 48 | int lastLineBegin = -1; 49 | for (int i = 0; i < s.length(); i++) { 50 | if (s.charAt(i) == '\n') { 51 | height++; 52 | if (i - lastLineBegin > maxWidth) { 53 | maxWidth = i - lastLineBegin - 1; 54 | } 55 | lastLineBegin = i; 56 | } 57 | } 58 | if (lastLineBegin < 0) { 59 | // the input string has no new line 60 | maxWidth = s.length(); 61 | } 62 | return Pair.of(maxWidth, height); 63 | } 64 | 65 | public Pair[] lines(final int width) { 66 | int count = getLineCount(width); 67 | Pair[] lines = new Pair[count]; 68 | Iterator> linesIterator = linesIterator(width); 69 | for (int i = 0; i < lines.length; i++) { 70 | if (linesIterator.hasNext()) { 71 | lines[i] = linesIterator.next(); 72 | } 73 | } 74 | return lines; 75 | } 76 | 77 | public int getLineCount(int width) { 78 | int index = 0; 79 | int count = 0; 80 | while (index < value.length()) { 81 | int pos = value.indexOf('\n', index); 82 | int nextIndex; 83 | if (pos == -1) { 84 | pos = Math.min(index + width, value.length()); 85 | nextIndex = pos; 86 | } else { 87 | if (pos <= index + width) { 88 | nextIndex = pos + 1; 89 | } else { 90 | nextIndex = index + width; 91 | } 92 | } 93 | count ++; 94 | index = nextIndex; 95 | } 96 | return count; 97 | } 98 | 99 | public Iterator> linesIterator(final int width) { 100 | if (width < 1) { 101 | throw new IllegalArgumentException("A non positive width=" + width + " cannot be accepted"); 102 | } 103 | return new BaseIterator>() { 104 | 105 | /** . */ 106 | int index = 0; 107 | 108 | /** . */ 109 | Pair next = null; 110 | 111 | public boolean hasNext() { 112 | if (next == null && index < value.length()) { 113 | int pos = value.indexOf('\n', index); 114 | int nextIndex; 115 | if (pos == -1) { 116 | pos = Math.min(index + width, value.length()); 117 | nextIndex = pos; 118 | } else { 119 | if (pos <= index + width) { 120 | nextIndex = pos + 1; 121 | } else { 122 | nextIndex = pos = index + width; 123 | } 124 | } 125 | next = Pair.of(index, pos); 126 | index = nextIndex; 127 | } 128 | return next != null; 129 | } 130 | 131 | public Pair next() { 132 | if (!hasNext()) { 133 | throw new NoSuchElementException(); 134 | } 135 | Pair next = this.next; 136 | this.next = null; 137 | return next; 138 | } 139 | }; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/util/Pair.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 eXo Platform SAS. 3 | * 4 | * This is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation; either version 2.1 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This software is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this software; if not, write to the Free 16 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 18 | */ 19 | 20 | package com.taobao.text.util; 21 | 22 | public class Pair { 23 | 24 | public static Pair of(F first, S second) { 25 | return new Pair(first, second); 26 | } 27 | 28 | /** . */ 29 | private final F first; 30 | 31 | /** . */ 32 | private final S second; 33 | 34 | public Pair(F first, S second) { 35 | this.first = first; 36 | this.second = second; 37 | } 38 | 39 | public F getFirst() { 40 | return first; 41 | } 42 | 43 | public S getSecond() { 44 | return second; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/taobao/text/util/RenderUtil.java: -------------------------------------------------------------------------------- 1 | package com.taobao.text.util; 2 | 3 | import java.io.IOException; 4 | import java.lang.reflect.Method; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.Comparator; 9 | import java.util.Iterator; 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | 13 | import com.taobao.text.Format; 14 | import com.taobao.text.LineReader; 15 | import com.taobao.text.LineRenderer; 16 | import com.taobao.text.RenderAppendable; 17 | import com.taobao.text.Renderer; 18 | import com.taobao.text.ScreenBuffer; 19 | import com.taobao.text.ScreenContext; 20 | import com.taobao.text.Screenable; 21 | import com.taobao.text.Style; 22 | import com.taobao.text.ui.BorderStyle; 23 | import com.taobao.text.ui.Element; 24 | import com.taobao.text.ui.RowElement; 25 | import com.taobao.text.ui.TableElement; 26 | 27 | /** 28 | * 29 | * @author duanling 2015年11月9日 下午11:56:03 30 | * 31 | */ 32 | public class RenderUtil { 33 | public static final int defaultWidth = 80; 34 | /** 35 | * 实际上这个参数通常不起作用 36 | */ 37 | public static final int defaultHeight = 80; 38 | 39 | /** 40 | * 格式化输出POJO对象,可以处理继承情况和boolean字段。字段按字母顺序排序 41 | * 42 | * @param list 43 | * @return 44 | */ 45 | static public String render(List list) { 46 | // 获取所有的get/is的函数和字段 47 | List fieldMethods = new LinkedList(); 48 | List fields = new ArrayList(); 49 | Object obj = list.get(0); 50 | for (Method method : obj.getClass().getMethods()) { 51 | if (method.getDeclaringClass().equals(Object.class)) { 52 | continue; 53 | } 54 | String methodName = method.getName(); 55 | int methodNameLength = methodName.length(); 56 | if (methodNameLength > "get".length() && methodName.startsWith("get")) { 57 | fieldMethods.add(method); 58 | String field = methodName.substring("get".length()); 59 | fields.add(field); 60 | } 61 | if (methodNameLength > "is".length() && methodName.startsWith("is")) { 62 | fieldMethods.add(method); 63 | String field = methodName.substring("is".length()); 64 | fields.add(field); 65 | } 66 | } 67 | 68 | if (fieldMethods.isEmpty()) { 69 | return "NULL"; 70 | } 71 | 72 | // 对fields排序 73 | Collections.sort(fields); 74 | 75 | // 对method排序 76 | Collections.sort(fieldMethods, new Comparator() { 77 | @Override 78 | public int compare(Method m1, Method m2) { 79 | return m1.getName().compareTo(m2.getName()); 80 | } 81 | }); 82 | 83 | return render(list, fields, fieldMethods); 84 | } 85 | 86 | /** 87 | * 格式化输出POJO对象,可以处理继承情况和boolean字段。 88 | * 89 | * @param list 90 | * @param fields 91 | * 要输出的字段列表 92 | * @return 93 | */ 94 | static public String render(List list, String[] fields) { 95 | if (list == null || fields == null || fields.length == 0) { 96 | return "NULL"; 97 | } 98 | if (list.size() == 0) { 99 | return "EMPTY LIST"; 100 | } 101 | // 先据field尝试获取get/is method,如果中间有获取出错,直接抛异常 102 | List fieldMethods = new ArrayList(fields.length); 103 | Object object = list.get(0); 104 | for (String field : fields) { 105 | String uppercaseFieldName = field; 106 | char first = field.charAt(0); 107 | if (first >= 'a' && first <= 'z') { 108 | first += 'A' - 'a'; 109 | uppercaseFieldName = first + field.substring(1); 110 | } 111 | Method method = null; 112 | try { 113 | method = object.getClass().getMethod("get" + uppercaseFieldName); 114 | } catch (Exception e) { 115 | // 尝试获取is函数 116 | try { 117 | method = object.getClass().getMethod("is" + uppercaseFieldName); 118 | } catch (Exception ee) { 119 | throw new RuntimeException("can not find get/is method!", ee); 120 | } 121 | } 122 | fieldMethods.add(method); 123 | } 124 | 125 | return render(list, Arrays.asList(fields), fieldMethods); 126 | } 127 | 128 | private static String render(List list, List fields, List fieldMethods) { 129 | TableElement tableElement = new TableElement().border(BorderStyle.DASHED).separator(BorderStyle.DASHED); 130 | 131 | tableElement.row(true, fields.toArray(new String[0])); 132 | 133 | for (O object : list) { 134 | RowElement row = Element.row(); 135 | for (Method method : fieldMethods) { 136 | String cell = null; 137 | try { 138 | Object callResult = method.invoke(object); 139 | if (callResult == null) { 140 | cell = "null"; 141 | } else { 142 | cell = callResult.toString(); 143 | } 144 | } catch (Exception e) { 145 | cell = "exception"; 146 | } 147 | row.add(Element.label(cell)); 148 | } 149 | tableElement.add(row); 150 | } 151 | return render(tableElement); 152 | } 153 | 154 | /** 155 | * 把Element 渲染为String,默认width是80 156 | * 157 | * @param element 158 | * @param width 159 | * @return 160 | */ 161 | static public String render(final Element element) { 162 | return render(element, defaultWidth); 163 | } 164 | 165 | /** 166 | * 渲染Iterator对象,用户自定义实现Renderer类 167 | * 168 | * @param iter 169 | * @param clazz 170 | * @return 171 | */ 172 | static public String render(Iterator iter, Renderer renderer) { 173 | return render(iter, renderer, defaultWidth); 174 | } 175 | 176 | /** 177 | * 渲染Iterator对象,用户自定义实现Renderer类 178 | * 179 | * @param iter 180 | * @param clazz 181 | * @param width 182 | * @return 183 | */ 184 | static public String render(Iterator iter, Renderer renderer, int width) { 185 | LineRenderer lineRenderer = renderer.renderer(iter); 186 | LineReader reader = lineRenderer.reader(width); 187 | return render(reader, width, defaultHeight); 188 | } 189 | 190 | /** 191 | * 渲染Iterator对象,用户自定义实现Renderer类 192 | * 193 | * @param iter 194 | * @param clazz 195 | * @param width 196 | * @param height 197 | * @return 198 | */ 199 | static public String render(Iterator iter, Renderer renderer, int width, int height) { 200 | LineRenderer lineRenderer = renderer.renderer(iter); 201 | LineReader reader = lineRenderer.reader(width, height); 202 | return render(reader, width, height); 203 | } 204 | 205 | /** 206 | * 把Element 渲染为String,高度是Element自然输出的高度 207 | * 208 | * @param element 209 | * @param width 210 | * @return 211 | */ 212 | static public String render(final Element element, final int width) { 213 | LineReader renderer = element.renderer().reader(width); 214 | return render(renderer, width, defaultHeight); 215 | } 216 | 217 | /** 218 | * 把Element 219 | * 渲染为String,当Element输出的高度小于height参数时,会填充空行。当Element高度大于height时,会截断。 220 | * 221 | * @param element 222 | * @param width 223 | * @param height 224 | * @return 225 | */ 226 | static public String render(final Element element, final int width, final int height) { 227 | LineReader renderer = element.renderer().reader(width, height); 228 | return render(renderer, width, height); 229 | } 230 | 231 | static public String render(final LineReader renderer, final int width, final int height) { 232 | StringBuilder result = new StringBuilder(2048); 233 | while (renderer.hasLine()) { 234 | final ScreenBuffer buffer = new ScreenBuffer(); 235 | renderer.renderLine(new RenderAppendable(new ScreenContext() { 236 | public int getWidth() { 237 | return width; 238 | } 239 | 240 | public int getHeight() { 241 | return height; 242 | } 243 | 244 | public Screenable append(CharSequence s) throws IOException { 245 | buffer.append(s); 246 | return this; 247 | } 248 | 249 | public Appendable append(char c) throws IOException { 250 | buffer.append(c); 251 | return this; 252 | } 253 | 254 | public Appendable append(CharSequence csq, int start, int end) throws IOException { 255 | buffer.append(csq, start, end); 256 | return this; 257 | } 258 | 259 | public Screenable append(Style style) throws IOException { 260 | buffer.append(style); 261 | return this; 262 | } 263 | 264 | public Screenable cls() throws IOException { 265 | buffer.cls(); 266 | return this; 267 | } 268 | 269 | public void flush() throws IOException { 270 | buffer.flush(); 271 | } 272 | })); 273 | StringBuilder sb = new StringBuilder(); 274 | try { 275 | buffer.format(Format.ANSI, sb); 276 | } catch (IOException e) { 277 | throw new RuntimeException(e); 278 | } 279 | result.append(sb.toString()).append('\n'); 280 | } 281 | return result.toString(); 282 | } 283 | 284 | /** 285 | * 清除字符串的里的所有的ansi颜色等字符 286 | * 287 | * @param str 288 | * @return 289 | */ 290 | static public String ansiToPlainText(String str) { 291 | if (str != null) { 292 | str = str.replaceAll("\u001B\\[[;\\d]*m", ""); 293 | } 294 | return str; 295 | } 296 | 297 | /** 298 | * 返回ansi的清屏字符串 299 | * 300 | * @return 301 | */ 302 | static public String cls() { 303 | /** 304 | * \033是控制字符,\033[H表示把光标移到(0,0),\033[2J表示清屏 305 | * 306 | */ 307 | return "\033[H\033[2J"; 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /src/main/resources/com/taobao/text/ui/themes/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |