GNU Classpath (0.20) | |
Frames | No Frames |
1: /* AbstractWriter.java -- 2: Copyright (C) 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.swing.text; 39: 40: import java.io.IOException; 41: import java.io.Writer; 42: import java.util.Arrays; 43: import java.util.Enumeration; 44: 45: /** 46: * This is an abstract base class for writing Document instances to a 47: * Writer. A concrete subclass must implement a method to iterate 48: * over the Elements of the Document and correctly format them. 49: */ 50: public abstract class AbstractWriter 51: { 52: /** 53: * The default line separator character. 54: * @specnote although this is a constant, it is not static in the JDK 55: */ 56: protected static final char NEWLINE = '\n'; 57: 58: // Where we write. 59: private Writer writer; 60: // How we iterate over the document. 61: private ElementIterator iter; 62: // The document over which we iterate. 63: private Document document; 64: // Maximum number of characters per line. 65: private int maxLineLength = 100; 66: // Number of characters we have currently written. 67: private int lineLength; 68: // True if we can apply line wrapping. 69: private boolean canWrapLines; // FIXME default? 70: // The number of spaces per indentation level. 71: private int indentSpace = 2; 72: // The current indentation level. 73: private int indentLevel; 74: // True if we have indented this line. 75: private boolean indented; 76: // Starting offset in document. 77: private int startOffset; 78: // Ending offset in document. 79: private int endOffset; 80: // The line separator string. 81: private String lineSeparator = "" + NEWLINE; 82: // The characters making up the line separator. 83: private char[] lineSeparatorChars = lineSeparator.toCharArray(); 84: 85: /** 86: * Create a new AbstractWriter with the indicated Writer and 87: * Document. The full range of the Document will be used. The 88: * internal ElementIterator will be initialized with the Document's 89: * root node. 90: */ 91: protected AbstractWriter(Writer writer, Document doc) 92: { 93: this.writer = writer; 94: this.iter = new ElementIterator(doc); 95: this.document = doc; 96: this.startOffset = 0; 97: this.endOffset = doc.getLength(); 98: } 99: 100: /** 101: * Create a new AbstractWriter with the indicated Writer and 102: * Document. The full range of the Document will be used. The 103: * internal ElementIterator will be initialized with the Document's 104: * root node. 105: */ 106: protected AbstractWriter(Writer writer, Document doc, int pos, int len) 107: { 108: this.writer = writer; 109: this.iter = new ElementIterator(doc); 110: this.document = doc; 111: this.startOffset = pos; 112: this.endOffset = pos + len; 113: } 114: 115: /** 116: * Create a new AbstractWriter with the indicated Writer and 117: * Element. The full range of the Element will be used. 118: */ 119: protected AbstractWriter(Writer writer, Element elt) 120: { 121: this.writer = writer; 122: this.iter = new ElementIterator(elt); 123: this.document = elt.getDocument(); 124: this.startOffset = elt.getStartOffset(); 125: this.endOffset = elt.getEndOffset(); 126: } 127: 128: /** 129: * Create a new AbstractWriter with the indicated Writer and 130: * Element. The full range of the Element will be used. The range 131: * will be limited to the indicated range of the Document. 132: */ 133: protected AbstractWriter(Writer writer, Element elt, int pos, int len) 134: { 135: this.writer = writer; 136: this.iter = new ElementIterator(elt); 137: this.document = elt.getDocument(); 138: this.startOffset = pos; 139: this.endOffset = pos + len; 140: } 141: 142: /** 143: * Return the ElementIterator for this writer. 144: */ 145: protected ElementIterator getElementIterator() 146: { 147: return iter; 148: } 149: 150: /** 151: * Return the Writer to which we are writing. 152: * @since 1.3 153: */ 154: protected Writer getWriter() 155: { 156: return writer; 157: } 158: 159: /** 160: * Return this writer's Document. 161: */ 162: protected Document getDocument() 163: { 164: return document; 165: } 166: 167: /** 168: * This method must be overridden by a concrete subclass. It is 169: * responsible for iterating over the Elements of the Document and 170: * writing them out. 171: */ 172: protected abstract void write() throws IOException, BadLocationException; 173: 174: /** 175: * Return the text of the Document that is associated with the given 176: * Element. If the Element is not a leaf Element, this will throw 177: * BadLocationException. 178: * 179: * @throws BadLocationException if the element is not a leaf 180: */ 181: protected String getText(Element elt) throws BadLocationException 182: { 183: if (! elt.isLeaf()) 184: throw new BadLocationException("Element is not a leaf", 185: elt.getStartOffset()); 186: return document.getText(elt.getStartOffset(), elt.getEndOffset()); 187: } 188: 189: /** 190: * This method calls Writer.write on the indicated data, and updates 191: * the current line length. This method does not look for newlines 192: * in the written data; the caller is responsible for that. 193: * 194: * @since 1.3 195: */ 196: protected void output(char[] data, int start, int len) throws IOException 197: { 198: writer.write(data, start, len); 199: lineLength += len; 200: } 201: 202: /** 203: * Write a line separator using the output method, and then reset 204: * the current line length. 205: * 206: * @since 1.3 207: */ 208: protected void writeLineSeparator() throws IOException 209: { 210: output(lineSeparatorChars, 0, lineSeparatorChars.length); 211: lineLength = 0; 212: indented = false; 213: } 214: 215: /** 216: * Write a single character. 217: */ 218: protected void write(char ch) throws IOException 219: { 220: write(new char[] { ch }, 0, 1); 221: } 222: 223: /** 224: * Write a String. 225: */ 226: protected void write(String s) throws IOException 227: { 228: char[] v = s.toCharArray(); 229: write(v, 0, v.length); 230: } 231: 232: /** 233: * Write a character array to the output Writer, properly handling 234: * newlines and, if needed, wrapping lines as they are output. 235: * @since 1.3 236: */ 237: protected void write(char[] data, int start, int len) throws IOException 238: { 239: if (getCanWrapLines()) 240: { 241: // FIXME: should we be handling newlines specially here? 242: for (int i = 0; i < len; ) 243: { 244: int start_i = i; 245: // Find next space. 246: while (i < len && data[start + i] != ' ') 247: ++i; 248: if (i < len && lineLength + i - start_i >= maxLineLength) 249: writeLineSeparator(); 250: else if (i < len) 251: { 252: // Write the trailing space. 253: ++i; 254: } 255: // Write out the text. 256: output(data, start + start_i, start + i - start_i); 257: } 258: } 259: else 260: { 261: int saved_i = start; 262: for (int i = start; i < start + len; ++i) 263: { 264: if (data[i] == NEWLINE) 265: { 266: output(data, saved_i, i - saved_i); 267: writeLineSeparator(); 268: } 269: } 270: if (saved_i < start + len - 1) 271: output(data, saved_i, start + len - saved_i); 272: } 273: } 274: 275: /** 276: * Indent this line by emitting spaces, according to the current 277: * indent level and the current number of spaces per indent. After 278: * this method is called, the current line is no longer considered 279: * to be empty, even if no spaces are actually written. 280: */ 281: protected void indent() throws IOException 282: { 283: int spaces = indentLevel * indentSpace; 284: if (spaces > 0) 285: { 286: char[] v = new char[spaces]; 287: Arrays.fill(v, ' '); 288: write(v, 0, v.length); 289: } 290: indented = true; 291: } 292: 293: /** 294: * Return the index of the Document at which output starts. 295: * @since 1.3 296: */ 297: public int getStartOffset() 298: { 299: return startOffset; 300: } 301: 302: /** 303: * Return the index of the Document at which output ends. 304: * @since 1.3 305: */ 306: public int getEndOffset() 307: { 308: return endOffset; 309: } 310: 311: /** 312: * Return true if the Element's range overlaps our desired output 313: * range; false otherwise. 314: */ 315: protected boolean inRange(Element elt) 316: { 317: int eltStart = elt.getStartOffset(); 318: int eltEnd = elt.getEndOffset(); 319: return ((eltStart >= startOffset && eltStart < endOffset) 320: || (eltEnd >= startOffset && eltEnd < endOffset)); 321: } 322: 323: /** 324: * Output the text of the indicated Element, properly clipping it to 325: * the range of the Document specified when the AbstractWriter was 326: * created. 327: */ 328: protected void text(Element elt) throws BadLocationException, IOException 329: { 330: int eltStart = elt.getStartOffset(); 331: int eltEnd = elt.getEndOffset(); 332: 333: eltStart = Math.max(eltStart, startOffset); 334: eltEnd = Math.min(eltEnd, endOffset); 335: write(document.getText(eltStart, eltEnd)); 336: } 337: 338: /** 339: * Set the maximum line length. 340: */ 341: protected void setLineLength(int maxLineLength) 342: { 343: this.maxLineLength = maxLineLength; 344: } 345: 346: /** 347: * Return the maximum line length. 348: * @since 1.3 349: */ 350: protected int getLineLength() 351: { 352: return maxLineLength; 353: } 354: 355: /** 356: * Set the current line length. 357: * @since 1.3 358: */ 359: protected void setCurrentLineLength(int lineLength) 360: { 361: this.lineLength = lineLength; 362: } 363: 364: /** 365: * Return the current line length. 366: * @since 1.3 367: */ 368: protected int getCurrentLineLength() 369: { 370: return lineLength; 371: } 372: 373: /** 374: * Return true if the line is empty, false otherwise. The line is 375: * empty if nothing has been written since the last newline, and 376: * indent has not been invoked. 377: */ 378: protected boolean isLineEmpty() 379: { 380: return lineLength == 0 && ! indented; 381: } 382: 383: /** 384: * Set the flag indicating whether lines will wrap. This affects 385: * the behavior of write(). 386: * @since 1.3 387: */ 388: protected void setCanWrapLines(boolean canWrapLines) 389: { 390: this.canWrapLines = canWrapLines; 391: } 392: 393: /** 394: * Return true if lines printed via write() will wrap, false 395: * otherwise. 396: * @since 1.3 397: */ 398: protected boolean getCanWrapLines() 399: { 400: return canWrapLines; 401: } 402: 403: /** 404: * Set the number of spaces per indent level. 405: * @since 1.3 406: */ 407: protected void setIndentSpace(int indentSpace) 408: { 409: this.indentSpace = indentSpace; 410: } 411: 412: /** 413: * Return the number of spaces per indent level. 414: * @since 1.3 415: */ 416: protected int getIndentSpace() 417: { 418: return indentSpace; 419: } 420: 421: /** 422: * Set the current line separator. 423: * @since 1.3 424: */ 425: public void setLineSeparator(String lineSeparator) 426: { 427: this.lineSeparator = lineSeparator; 428: this.lineSeparatorChars = lineSeparator.toCharArray(); 429: } 430: 431: /** 432: * Return the current line separator. 433: * @since 1.3 434: */ 435: public String getLineSeparator() 436: { 437: return lineSeparator; 438: } 439: 440: /** 441: * Increment the indent level. 442: */ 443: protected void incrIndent() 444: { 445: ++indentLevel; 446: } 447: 448: /** 449: * Decrement the indent level. 450: */ 451: protected void decrIndent() 452: { 453: --indentLevel; 454: } 455: 456: /** 457: * Return the current indent level. 458: * @since 1.3 459: */ 460: protected int getIndentLevel() 461: { 462: return indentLevel; 463: } 464: 465: /** 466: * Print the given AttributeSet as a sequence of assignment-like 467: * strings, e.g. "key=value". 468: */ 469: protected void writeAttributes(AttributeSet attrs) throws IOException 470: { 471: Enumeration e = attrs.getAttributeNames(); 472: while (e.hasMoreElements()) 473: { 474: Object name = e.nextElement(); 475: Object val = attrs.getAttribute(name); 476: write(name + "=" + val); 477: writeLineSeparator(); 478: } 479: } 480: }
GNU Classpath (0.20) |