1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * As per the Apache license requirements, this file has been modified 19 * from its original state. 20 * 21 * Such modifications are Copyright (C) 2010 Ben Gruver, and are released 22 * under the original license 23 */ 24 25package org.jf.dexlib.Util; 26 27import java.io.*; 28 29/** 30 * Class that takes a combined output destination and provides two 31 * output writers, one of which ends up writing to the left column and 32 * one which goes on the right. 33 */ 34public final class TwoColumnOutput { 35 /** non-null; underlying writer for final output */ 36 private final Writer out; 37 38 /** > 0; the left column width */ 39 private final int leftWidth; 40 41 /** non-null; pending left column output */ 42 private final StringBuffer leftBuf; 43 44 /** non-null; pending right column output */ 45 private final StringBuffer rightBuf; 46 47 /** non-null; left column writer */ 48 private final IndentingWriter leftColumn; 49 50 /** non-null; right column writer */ 51 private final IndentingWriter rightColumn; 52 53 /** 54 * Turns the given two strings (with widths) and spacer into a formatted 55 * two-column string. 56 * 57 * @param s1 non-null; first string 58 * @param width1 > 0; width of the first column 59 * @param spacer non-null; spacer string 60 * @param s2 non-null; second string 61 * @param width2 > 0; width of the second column 62 * @return non-null; an appropriately-formatted string 63 */ 64 public static String toString(String s1, int width1, String spacer, 65 String s2, int width2) { 66 int len1 = s1.length(); 67 int len2 = s2.length(); 68 69 StringWriter sw = new StringWriter((len1 + len2) * 3); 70 TwoColumnOutput twoOut = 71 new TwoColumnOutput(sw, width1, width2, spacer); 72 73 try { 74 twoOut.getLeft().write(s1); 75 twoOut.getRight().write(s2); 76 } catch (IOException ex) { 77 throw new RuntimeException("shouldn't happen", ex); 78 } 79 80 twoOut.flush(); 81 return sw.toString(); 82 } 83 84 /** 85 * Constructs an instance. 86 * 87 * @param out non-null; writer to send final output to 88 * @param leftWidth > 0; width of the left column, in characters 89 * @param rightWidth > 0; width of the right column, in characters 90 * @param spacer non-null; spacer string to sit between the two columns 91 */ 92 public TwoColumnOutput(Writer out, int leftWidth, int rightWidth, 93 String spacer) { 94 if (out == null) { 95 throw new NullPointerException("out == null"); 96 } 97 98 if (leftWidth < 1) { 99 throw new IllegalArgumentException("leftWidth < 1"); 100 } 101 102 if (rightWidth < 1) { 103 throw new IllegalArgumentException("rightWidth < 1"); 104 } 105 106 if (spacer == null) { 107 throw new NullPointerException("spacer == null"); 108 } 109 110 StringWriter leftWriter = new StringWriter(1000); 111 StringWriter rightWriter = new StringWriter(1000); 112 113 this.out = out; 114 this.leftWidth = leftWidth; 115 this.leftBuf = leftWriter.getBuffer(); 116 this.rightBuf = rightWriter.getBuffer(); 117 this.leftColumn = new IndentingWriter(leftWriter, leftWidth); 118 this.rightColumn = 119 new IndentingWriter(rightWriter, rightWidth, spacer); 120 } 121 122 /** 123 * Constructs an instance. 124 * 125 * @param out non-null; stream to send final output to 126 * @param leftWidth >= 1; width of the left column, in characters 127 * @param rightWidth >= 1; width of the right column, in characters 128 * @param spacer non-null; spacer string to sit between the two columns 129 */ 130 public TwoColumnOutput(OutputStream out, int leftWidth, int rightWidth, 131 String spacer) { 132 this(new OutputStreamWriter(out), leftWidth, rightWidth, spacer); 133 } 134 135 /** 136 * Gets the writer to use to write to the left column. 137 * 138 * @return non-null; the left column writer 139 */ 140 public Writer getLeft() { 141 return leftColumn; 142 } 143 144 /** 145 * Gets the writer to use to write to the right column. 146 * 147 * @return non-null; the right column writer 148 */ 149 public Writer getRight() { 150 return rightColumn; 151 } 152 153 /** 154 * Flushes the output. If there are more lines of pending output in one 155 * column, then the other column will get filled with blank lines. 156 */ 157 public void flush() { 158 try { 159 appendNewlineIfNecessary(leftBuf, leftColumn); 160 appendNewlineIfNecessary(rightBuf, rightColumn); 161 outputFullLines(); 162 flushLeft(); 163 flushRight(); 164 } catch (IOException ex) { 165 throw new RuntimeException(ex); 166 } 167 } 168 169 /** 170 * Outputs to the final destination as many full line pairs as 171 * there are in the pending output, removing those lines from 172 * their respective buffers. This method terminates when at 173 * least one of the two column buffers is empty. 174 */ 175 private void outputFullLines() throws IOException { 176 for (;;) { 177 int leftLen = leftBuf.indexOf("\n"); 178 if (leftLen < 0) { 179 return; 180 } 181 182 int rightLen = rightBuf.indexOf("\n"); 183 if (rightLen < 0) { 184 return; 185 } 186 187 if (leftLen != 0) { 188 out.write(leftBuf.substring(0, leftLen)); 189 } 190 191 if (rightLen != 0) { 192 writeSpaces(out, leftWidth - leftLen); 193 out.write(rightBuf.substring(0, rightLen)); 194 } 195 196 out.write('\n'); 197 198 leftBuf.delete(0, leftLen + 1); 199 rightBuf.delete(0, rightLen + 1); 200 } 201 } 202 203 /** 204 * Flushes the left column buffer, printing it and clearing the buffer. 205 * If the buffer is already empty, this does nothing. 206 */ 207 private void flushLeft() throws IOException { 208 appendNewlineIfNecessary(leftBuf, leftColumn); 209 210 while (leftBuf.length() != 0) { 211 rightColumn.write('\n'); 212 outputFullLines(); 213 } 214 } 215 216 /** 217 * Flushes the right column buffer, printing it and clearing the buffer. 218 * If the buffer is already empty, this does nothing. 219 */ 220 private void flushRight() throws IOException { 221 appendNewlineIfNecessary(rightBuf, rightColumn); 222 223 while (rightBuf.length() != 0) { 224 leftColumn.write('\n'); 225 outputFullLines(); 226 } 227 } 228 229 /** 230 * Appends a newline to the given buffer via the given writer, but 231 * only if it isn't empty and doesn't already end with one. 232 * 233 * @param buf non-null; the buffer in question 234 * @param out non-null; the writer to use 235 */ 236 private static void appendNewlineIfNecessary(StringBuffer buf, 237 Writer out) 238 throws IOException { 239 int len = buf.length(); 240 241 if ((len != 0) && (buf.charAt(len - 1) != '\n')) { 242 out.write('\n'); 243 } 244 } 245 246 /** 247 * Writes the given number of spaces to the given writer. 248 * 249 * @param out non-null; where to write 250 * @param amt >= 0; the number of spaces to write 251 */ 252 private static void writeSpaces(Writer out, int amt) throws IOException { 253 while (amt > 0) { 254 out.write(' '); 255 amt--; 256 } 257 } 258} 259