History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/text/AbstractWriter.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/text/AbstractWriter.java+713−0
@@ -0,0 +1,713 @@
1/*
2 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing.text;
27
28import java.io.Writer;
29import java.io.IOException;
30import java.util.Enumeration;
31
32/**
33 * AbstractWriter is an abstract class that actually
34 * does the work of writing out the element tree
35 * including the attributes. In terms of how much is
36 * written out per line, the writer defaults to 100.
37 * But this value can be set by subclasses.
38 *
39 * @author Sunita Mani
40 */
41
42public abstract class AbstractWriter {
43
44 private ElementIterator it;
45 private Writer out;
46 private int indentLevel = 0;
47 private int indentSpace = 2;
48 private Document doc = null;
49 private int maxLineLength = 100;
50 private int currLength = 0;
51 private int startOffset = 0;
52 private int endOffset = 0;
53 // If (indentLevel * indentSpace) becomes >= maxLineLength, this will
54 // get incremened instead of indentLevel to avoid indenting going greater
55 // than line length.
56 private int offsetIndent = 0;
57
58 /**
59 * String used for end of line. If the Document has the property
60 * EndOfLineStringProperty, it will be used for newlines. Otherwise
61 * the System property line.separator will be used. The line separator
62 * can also be set.
63 */
64 private String lineSeparator;
65
66 /**
67 * True indicates that when writing, the line can be split, false
68 * indicates that even if the line is > than max line length it should
69 * not be split.
70 */
71 private boolean canWrapLines;
72
73 /**
74 * True while the current line is empty. This will remain true after
75 * indenting.
76 */
77 private boolean isLineEmpty;
78
79 /**
80 * Used when indenting. Will contain the spaces.
81 */
82 private char[] indentChars;
83
84 /**
85 * Used when writing out a string.
86 */
87 private char[] tempChars;
88
89 /**
90 * This is used in <code>writeLineSeparator</code> instead of
91 * tempChars. If tempChars were used it would mean write couldn't invoke
92 * <code>writeLineSeparator</code> as it might have been passed
93 * tempChars.
94 */
95 private char[] newlineChars;
96
97 /**
98 * Used for writing text.
99 */
100 private Segment segment;
101
102 /**
103 * How the text packages models newlines.
104 * @see #getLineSeparator
105 */
106 protected static final char NEWLINE = '\n';
107
108
109 /**
110 * Creates a new AbstractWriter.
111 * Initializes the ElementIterator with the default
112 * root of the document.
113 *
114 * @param w a Writer.
115 * @param doc a Document
116 */
117 protected AbstractWriter(Writer w, Document doc) {
118 this(w, doc, 0, doc.getLength());
119 }
120
121 /**
122 * Creates a new AbstractWriter.
123 * Initializes the ElementIterator with the
124 * element passed in.
125 *
126 * @param w a Writer
127 * @param doc an Element
128 * @param pos The location in the document to fetch the
129 * content.
130 * @param len The amount to write out.
131 */
132 protected AbstractWriter(Writer w, Document doc, int pos, int len) {
133 this.doc = doc;
134 it = new ElementIterator(doc.getDefaultRootElement());
135 out = w;
136 startOffset = pos;
137 endOffset = pos + len;
138 Object docNewline = doc.getProperty(DefaultEditorKit.
139 EndOfLineStringProperty);
140 if (docNewline instanceof String) {
141 setLineSeparator((String)docNewline);
142 }
143 else {
144 String newline = null;
145 try {
146 newline = System.getProperty("line.separator");
147 } catch (SecurityException se) {}
148 if (newline == null) {
149 // Should not get here, but if we do it means we could not
150 // find a newline string, use \n in this case.
151 newline = "\n";
152 }
153 setLineSeparator(newline);
154 }
155 canWrapLines = true;
156 }
157
158 /**
159 * Creates a new AbstractWriter.
160 * Initializes the ElementIterator with the
161 * element passed in.
162 *
163 * @param w a Writer
164 * @param root an Element
165 */
166 protected AbstractWriter(Writer w, Element root) {
167 this(w, root, 0, root.getEndOffset());
168 }
169
170 /**
171 * Creates a new AbstractWriter.
172 * Initializes the ElementIterator with the
173 * element passed in.
174 *
175 * @param w a Writer
176 * @param root an Element
177 * @param pos The location in the document to fetch the
178 * content.
179 * @param len The amount to write out.
180 */
181 protected AbstractWriter(Writer w, Element root, int pos, int len) {
182 this.doc = root.getDocument();
183 it = new ElementIterator(root);
184 out = w;
185 startOffset = pos;
186 endOffset = pos + len;
187 canWrapLines = true;
188 }
189
190 /**
191 * Returns the first offset to be output.
192 *
193 * @since 1.3
194 */
195 public int getStartOffset() {
196 return startOffset;
197 }
198
199 /**
200 * Returns the last offset to be output.
201 *
202 * @since 1.3
203 */
204 public int getEndOffset() {
205 return endOffset;
206 }
207
208 /**
209 * Fetches the ElementIterator.
210 *
211 * @return the ElementIterator.
212 */
213 protected ElementIterator getElementIterator() {
214 return it;
215 }
216
217 /**
218 * Returns the Writer that is used to output the content.
219 *
220 * @since 1.3
221 */
222 protected Writer getWriter() {
223 return out;
224 }
225
226 /**
227 * Fetches the document.
228 *
229 * @return the Document.
230 */
231 protected Document getDocument() {
232 return doc;
233 }
234
235 /**
236 * This method determines whether the current element
237 * is in the range specified. When no range is specified,
238 * the range is initialized to be the entire document.
239 * inRange() returns true if the range specified intersects
240 * with the element's range.
241 *
242 * @param next an Element.
243 * @return boolean that indicates whether the element
244 * is in the range.
245 */
246 protected boolean inRange(Element next) {
247 int startOffset = getStartOffset();
248 int endOffset = getEndOffset();
249 if ((next.getStartOffset() >= startOffset &&
250 next.getStartOffset() < endOffset) ||
251 (startOffset >= next.getStartOffset() &&
252 startOffset < next.getEndOffset())) {
253 return true;
254 }
255 return false;
256 }
257
258 /**
259 * This abstract method needs to be implemented
260 * by subclasses. Its responsibility is to
261 * iterate over the elements and use the write()
262 * methods to generate output in the desired format.
263 */
264 abstract protected void write() throws IOException, BadLocationException;
265
266 /**
267 * Returns the text associated with the element.
268 * The assumption here is that the element is a
269 * leaf element. Throws a BadLocationException
270 * when encountered.
271 *
272 * @param elem an <code>Element</code>
273 * @exception BadLocationException if pos represents an invalid
274 * location within the document
275 * @return the text as a <code>String</code>
276 */
277 protected String getText(Element elem) throws BadLocationException {
278 return doc.getText(elem.getStartOffset(),
279 elem.getEndOffset() - elem.getStartOffset());
280 }
281
282
283 /**
284 * Writes out text. If a range is specified when the constructor
285 * is invoked, then only the appropriate range of text is written
286 * out.
287 *
288 * @param elem an Element.
289 * @exception IOException on any I/O error
290 * @exception BadLocationException if pos represents an invalid
291 * location within the document.
292 */
293 protected void text(Element elem) throws BadLocationException,
294 IOException {
295 int start = Math.max(getStartOffset(), elem.getStartOffset());
296 int end = Math.min(getEndOffset(), elem.getEndOffset());
297 if (start < end) {
298 if (segment == null) {
299 segment = new Segment();
300 }
301 getDocument().getText(start, end - start, segment);
302 if (segment.count > 0) {
303 write(segment.array, segment.offset, segment.count);
304 }
305 }
306 }
307
308 /**
309 * Enables subclasses to set the number of characters they
310 * want written per line. The default is 100.
311 *
312 * @param l the maximum line length.
313 */
314 protected void setLineLength(int l) {
315 maxLineLength = l;
316 }
317
318 /**
319 * Returns the maximum line length.
320 *
321 * @since 1.3
322 */
323 protected int getLineLength() {
324 return maxLineLength;
325 }
326
327 /**
328 * Sets the current line length.
329 *
330 * @since 1.3
331 */
332 protected void setCurrentLineLength(int length) {
333 currLength = length;
334 isLineEmpty = (currLength == 0);
335 }
336
337 /**
338 * Returns the current line length.
339 *
340 * @since 1.3
341 */
342 protected int getCurrentLineLength() {
343 return currLength;
344 }
345
346 /**
347 * Returns true if the current line should be considered empty. This
348 * is true when <code>getCurrentLineLength</code> == 0 ||
349 * <code>indent</code> has been invoked on an empty line.
350 *
351 * @since 1.3
352 */
353 protected boolean isLineEmpty() {
354 return isLineEmpty;
355 }
356
357 /**
358 * Sets whether or not lines can be wrapped. This can be toggled
359 * during the writing of lines. For example, outputting HTML might
360 * set this to false when outputting a quoted string.
361 *
362 * @since 1.3
363 */
364 protected void setCanWrapLines(boolean newValue) {
365 canWrapLines = newValue;
366 }
367
368 /**
369 * Returns whether or not the lines can be wrapped. If this is false
370 * no lineSeparator's will be output.
371 *
372 * @since 1.3
373 */
374 protected boolean getCanWrapLines() {
375 return canWrapLines;
376 }
377
378 /**
379 * Enables subclasses to specify how many spaces an indent
380 * maps to. When indentation takes place, the indent level
381 * is multiplied by this mapping. The default is 2.
382 *
383 * @param space an int representing the space to indent mapping.
384 */
385 protected void setIndentSpace(int space) {
386 indentSpace = space;
387 }
388
389 /**
390 * Returns the amount of space to indent.
391 *
392 * @since 1.3
393 */
394 protected int getIndentSpace() {
395 return indentSpace;
396 }
397
398 /**
399 * Sets the String used to reprsent newlines. This is initialized
400 * in the constructor from either the Document, or the System property
401 * line.separator.
402 *
403 * @since 1.3
404 */
405 public void setLineSeparator(String value) {
406 lineSeparator = value;
407 }
408
409 /**
410 * Returns the string used to represent newlines.
411 *
412 * @since 1.3
413 */
414 public String getLineSeparator() {
415 return lineSeparator;
416 }
417
418 /**
419 * Increments the indent level. If indenting would cause
420 * <code>getIndentSpace()</code> *<code>getIndentLevel()</code> to be >
421 * than <code>getLineLength()</code> this will not cause an indent.
422 */
423 protected void incrIndent() {
424 // Only increment to a certain point.
425 if (offsetIndent > 0) {
426 offsetIndent++;
427 }
428 else {
429 if (++indentLevel * getIndentSpace() >= getLineLength()) {
430 offsetIndent++;
431 --indentLevel;
432 }
433 }
434 }
435
436 /**
437 * Decrements the indent level.
438 */
439 protected void decrIndent() {
440 if (offsetIndent > 0) {
441 --offsetIndent;
442 }
443 else {
444 indentLevel--;
445 }
446 }
447
448 /**
449 * Returns the current indentation level. That is, the number of times
450 * <code>incrIndent</code> has been invoked minus the number of times
451 * <code>decrIndent</code> has been invoked.
452 *
453 * @since 1.3
454 */
455 protected int getIndentLevel() {
456 return indentLevel;
457 }
458
459 /**
460 * Does indentation. The number of spaces written
461 * out is indent level times the space to map mapping. If the current
462 * line is empty, this will not make it so that the current line is
463 * still considered empty.
464 *
465 * @exception IOException on any I/O error
466 */
467 protected void indent() throws IOException {
468 int max = getIndentLevel() * getIndentSpace();
469 if (indentChars == null || max > indentChars.length) {
470 indentChars = new char[max];
471 for (int counter = 0; counter < max; counter++) {
472 indentChars[counter] = ' ';
473 }
474 }
475 int length = getCurrentLineLength();
476 boolean wasEmpty = isLineEmpty();
477 output(indentChars, 0, max);
478 if (wasEmpty && length == 0) {
479 isLineEmpty = true;
480 }
481 }
482
483 /**
484 * Writes out a character. This is implemented to invoke
485 * the <code>write</code> method that takes a char[].
486 *
487 * @param ch a char.
488 * @exception IOException on any I/O error
489 */
490 protected void write(char ch) throws IOException {
491 if (tempChars == null) {
492 tempChars = new char[128];
493 }
494 tempChars[0] = ch;
495 write(tempChars, 0, 1);
496 }
497
498 /**
499 * Writes out a string. This is implemented to invoke the
500 * <code>write</code> method that takes a char[].
501 *
502 * @param content a String.
503 * @exception IOException on any I/O error
504 */
505 protected void write(String content) throws IOException {
506 if (content == null) {
507 return;
508 }
509 int size = content.length();
510 if (tempChars == null || tempChars.length < size) {
511 tempChars = new char[size];
512 }
513 content.getChars(0, size, tempChars, 0);
514 write(tempChars, 0, size);
515 }
516
517 /**
518 * Writes the line separator. This invokes <code>output</code> directly
519 * as well as setting the <code>lineLength</code> to 0.
520 *
521 * @since 1.3
522 */
523 protected void writeLineSeparator() throws IOException {
524 String newline = getLineSeparator();
525 int length = newline.length();
526 if (newlineChars == null || newlineChars.length < length) {
527 newlineChars = new char[length];
528 }
529 newline.getChars(0, length, newlineChars, 0);
530 output(newlineChars, 0, length);
531 setCurrentLineLength(0);
532 }
533
534 /**
535 * All write methods call into this one. If <code>getCanWrapLines()</code>
536 * returns false, this will call <code>output</code> with each sequence
537 * of <code>chars</code> that doesn't contain a NEWLINE, followed
538 * by a call to <code>writeLineSeparator</code>. On the other hand,
539 * if <code>getCanWrapLines()</code> returns true, this will split the
540 * string, as necessary, so <code>getLineLength</code> is honored.
541 * The only exception is if the current string contains no whitespace,
542 * and won't fit in which case the line length will exceed
543 * <code>getLineLength</code>.
544 *
545 * @since 1.3
546 */
547 protected void write(char[] chars, int startIndex, int length)
548 throws IOException {
549 if (!getCanWrapLines()) {
550 // We can not break string, just track if a newline
551 // is in it.
552 int lastIndex = startIndex;
553 int endIndex = startIndex + length;
554 int newlineIndex = indexOf(chars, NEWLINE, startIndex, endIndex);
555 while (newlineIndex != -1) {
556 if (newlineIndex > lastIndex) {
557 output(chars, lastIndex, newlineIndex - lastIndex);
558 }
559 writeLineSeparator();
560 lastIndex = newlineIndex + 1;
561 newlineIndex = indexOf(chars, '\n', lastIndex, endIndex);
562 }
563 if (lastIndex < endIndex) {
564 output(chars, lastIndex, endIndex - lastIndex);
565 }
566 }
567 else {
568 // We can break chars if the length exceeds maxLength.
569 int lastIndex = startIndex;
570 int endIndex = startIndex + length;
571 int lineLength = getCurrentLineLength();
572 int maxLength = getLineLength();
573
574 while (lastIndex < endIndex) {
575 int newlineIndex = indexOf(chars, NEWLINE, lastIndex,
576 endIndex);
577 boolean needsNewline = false;
578 boolean forceNewLine = false;
579
580 lineLength = getCurrentLineLength();
581 if (newlineIndex != -1 && (lineLength +
582 (newlineIndex - lastIndex)) < maxLength) {
583 if (newlineIndex > lastIndex) {
584 output(chars, lastIndex, newlineIndex - lastIndex);
585 }
586 lastIndex = newlineIndex + 1;
587 forceNewLine = true;
588 }
589 else if (newlineIndex == -1 && (lineLength +
590 (endIndex - lastIndex)) < maxLength) {
591 if (endIndex > lastIndex) {
592 output(chars, lastIndex, endIndex - lastIndex);
593 }
594 lastIndex = endIndex;
595 }
596 else {
597 // Need to break chars, find a place to split chars at,
598 // from lastIndex to endIndex,
599 // or maxLength - lineLength whichever is smaller
600 int breakPoint = -1;
601 int maxBreak = Math.min(endIndex - lastIndex,
602 maxLength - lineLength - 1);
603 int counter = 0;
604 while (counter < maxBreak) {
605 if (Character.isWhitespace(chars[counter +
606 lastIndex])) {
607 breakPoint = counter;
608 }
609 counter++;
610 }
611 if (breakPoint != -1) {
612 // Found a place to break at.
613 breakPoint += lastIndex + 1;
614 output(chars, lastIndex, breakPoint - lastIndex);
615 lastIndex = breakPoint;
616 needsNewline = true;
617 }
618 else {
619 // No where good to break.
620
621 // find the next whitespace, or write out the
622 // whole string.
623 // maxBreak will be negative if current line too
624 // long.
625 counter = Math.max(0, maxBreak);
626 maxBreak = endIndex - lastIndex;
627 while (counter < maxBreak) {
628 if (Character.isWhitespace(chars[counter +
629 lastIndex])) {
630 breakPoint = counter;
631 break;
632 }
633 counter++;
634 }
635 if (breakPoint == -1) {
636 output(chars, lastIndex, endIndex - lastIndex);
637 breakPoint = endIndex;
638 }
639 else {
640 breakPoint += lastIndex;
641 if (chars[breakPoint] == NEWLINE) {
642 output(chars, lastIndex, breakPoint++ -
643 lastIndex);
644 forceNewLine = true;
645 }
646 else {
647 output(chars, lastIndex, ++breakPoint -
648 lastIndex);
649 needsNewline = true;
650 }
651 }
652 lastIndex = breakPoint;
653 }
654 }
655 if (forceNewLine || needsNewline || lastIndex < endIndex) {
656 writeLineSeparator();
657 if (lastIndex < endIndex || !forceNewLine) {
658 indent();
659 }
660 }
661 }
662 }
663 }
664
665 /**
666 * Writes out the set of attributes as " <name>=<value>"
667 * pairs. It throws an IOException when encountered.
668 *
669 * @param attr an AttributeSet.
670 * @exception IOException on any I/O error
671 */
672 protected void writeAttributes(AttributeSet attr) throws IOException {
673
674 Enumeration names = attr.getAttributeNames();
675 while (names.hasMoreElements()) {
676 Object name = names.nextElement();
677 write(" " + name + "=" + attr.getAttribute(name));
678 }
679 }
680
681 /**
682 * The last stop in writing out content. All the write methods eventually
683 * make it to this method, which invokes <code>write</code> on the
684 * Writer.
685 * <p>This method also updates the line length based on
686 * <code>length</code>. If this is invoked to output a newline, the
687 * current line length will need to be reset as will no longer be
688 * valid. If it is up to the caller to do this. Use
689 * <code>writeLineSeparator</code> to write out a newline, which will
690 * property update the current line length.
691 *
692 * @since 1.3
693 */
694 protected void output(char[] content, int start, int length)
695 throws IOException {
696 getWriter().write(content, start, length);
697 setCurrentLineLength(getCurrentLineLength() + length);
698 }
699
700 /**
701 * Support method to locate an occurence of a particular character.
702 */
703 private int indexOf(char[] chars, char sChar, int startIndex,
704 int endIndex) {
705 while(startIndex < endIndex) {
706 if (chars[startIndex] == sChar) {
707 return startIndex;
708 }
709 startIndex++;
710 }
711 return -1;
712 }
713}