001 /* 002 * Copyright (C) 2012 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 020 package org.crsh.text; 021 022 import java.util.Iterator; 023 024 /** 025 * A line oriented renderer. 026 */ 027 public abstract class LineRenderer { 028 029 public static final LineRenderer NULL = new LineRenderer() { 030 @Override 031 public int getActualWidth() { 032 return 0; 033 } 034 @Override 035 public int getMinWidth() { 036 return 0; 037 } 038 @Override 039 public int getMinHeight(int width) { 040 return 0; 041 } 042 @Override 043 public int getActualHeight(int width) { 044 return 0; 045 } 046 @Override 047 public LineReader reader(int width) { 048 return new LineReader() { 049 public boolean hasLine() { 050 return false; 051 } 052 public void renderLine(RenderAppendable to) throws IllegalStateException { 053 throw new IllegalStateException(); 054 } 055 }; 056 } 057 }; 058 059 public static LineRenderer vertical(Iterable<? extends LineRenderer> renderers) { 060 Iterator<? extends LineRenderer> i = renderers.iterator(); 061 if (i.hasNext()) { 062 LineRenderer renderer = i.next(); 063 if (i.hasNext()) { 064 return new Composite(renderers); 065 } else { 066 return renderer; 067 } 068 } else { 069 return NULL; 070 } 071 } 072 073 /** 074 * Returns the element actual width. 075 * 076 * @return the actual width 077 */ 078 public abstract int getActualWidth(); 079 080 /** 081 * Returns the element minimum width. 082 * 083 * @return the minimum width 084 */ 085 public abstract int getMinWidth(); 086 087 /** 088 * Return the minimum height for the specified with. 089 * 090 * @param width the width 091 * @return the actual height 092 */ 093 public abstract int getMinHeight(int width); 094 095 /** 096 * Return the actual height for the specified with. 097 * 098 * @param width the width 099 * @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 * <code>height</code> 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<? extends LineRenderer> renderers; 148 149 /** . */ 150 private final int actualWidth; 151 152 /** . */ 153 private final int minWidth; 154 155 private Composite(Iterable<? extends LineRenderer> 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<? extends LineRenderer> 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 }