0N/A/*
2362N/A * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage javax.swing.text;
0N/A
0N/Aimport java.io.Writer;
0N/Aimport java.io.IOException;
0N/Aimport java.util.Enumeration;
0N/A
0N/A/**
0N/A * AbstractWriter is an abstract class that actually
0N/A * does the work of writing out the element tree
0N/A * including the attributes. In terms of how much is
0N/A * written out per line, the writer defaults to 100.
0N/A * But this value can be set by subclasses.
0N/A *
0N/A * @author Sunita Mani
0N/A */
0N/A
0N/Apublic abstract class AbstractWriter {
0N/A
0N/A private ElementIterator it;
0N/A private Writer out;
0N/A private int indentLevel = 0;
0N/A private int indentSpace = 2;
0N/A private Document doc = null;
0N/A private int maxLineLength = 100;
0N/A private int currLength = 0;
0N/A private int startOffset = 0;
0N/A private int endOffset = 0;
0N/A // If (indentLevel * indentSpace) becomes >= maxLineLength, this will
0N/A // get incremened instead of indentLevel to avoid indenting going greater
0N/A // than line length.
0N/A private int offsetIndent = 0;
0N/A
0N/A /**
0N/A * String used for end of line. If the Document has the property
0N/A * EndOfLineStringProperty, it will be used for newlines. Otherwise
0N/A * the System property line.separator will be used. The line separator
0N/A * can also be set.
0N/A */
0N/A private String lineSeparator;
0N/A
0N/A /**
0N/A * True indicates that when writing, the line can be split, false
0N/A * indicates that even if the line is > than max line length it should
0N/A * not be split.
0N/A */
0N/A private boolean canWrapLines;
0N/A
0N/A /**
0N/A * True while the current line is empty. This will remain true after
0N/A * indenting.
0N/A */
0N/A private boolean isLineEmpty;
0N/A
0N/A /**
0N/A * Used when indenting. Will contain the spaces.
0N/A */
0N/A private char[] indentChars;
0N/A
0N/A /**
0N/A * Used when writing out a string.
0N/A */
0N/A private char[] tempChars;
0N/A
0N/A /**
0N/A * This is used in <code>writeLineSeparator</code> instead of
0N/A * tempChars. If tempChars were used it would mean write couldn't invoke
0N/A * <code>writeLineSeparator</code> as it might have been passed
0N/A * tempChars.
0N/A */
0N/A private char[] newlineChars;
0N/A
0N/A /**
0N/A * Used for writing text.
0N/A */
0N/A private Segment segment;
0N/A
0N/A /**
0N/A * How the text packages models newlines.
0N/A * @see #getLineSeparator
0N/A */
0N/A protected static final char NEWLINE = '\n';
0N/A
0N/A
0N/A /**
0N/A * Creates a new AbstractWriter.
0N/A * Initializes the ElementIterator with the default
0N/A * root of the document.
0N/A *
0N/A * @param w a Writer.
0N/A * @param doc a Document
0N/A */
0N/A protected AbstractWriter(Writer w, Document doc) {
0N/A this(w, doc, 0, doc.getLength());
0N/A }
0N/A
0N/A /**
0N/A * Creates a new AbstractWriter.
0N/A * Initializes the ElementIterator with the
0N/A * element passed in.
0N/A *
0N/A * @param w a Writer
0N/A * @param doc an Element
0N/A * @param pos The location in the document to fetch the
0N/A * content.
0N/A * @param len The amount to write out.
0N/A */
0N/A protected AbstractWriter(Writer w, Document doc, int pos, int len) {
0N/A this.doc = doc;
0N/A it = new ElementIterator(doc.getDefaultRootElement());
0N/A out = w;
0N/A startOffset = pos;
0N/A endOffset = pos + len;
0N/A Object docNewline = doc.getProperty(DefaultEditorKit.
0N/A EndOfLineStringProperty);
0N/A if (docNewline instanceof String) {
0N/A setLineSeparator((String)docNewline);
0N/A }
0N/A else {
0N/A String newline = null;
0N/A try {
0N/A newline = System.getProperty("line.separator");
0N/A } catch (SecurityException se) {}
0N/A if (newline == null) {
0N/A // Should not get here, but if we do it means we could not
0N/A // find a newline string, use \n in this case.
0N/A newline = "\n";
0N/A }
0N/A setLineSeparator(newline);
0N/A }
0N/A canWrapLines = true;
0N/A }
0N/A
0N/A /**
0N/A * Creates a new AbstractWriter.
0N/A * Initializes the ElementIterator with the
0N/A * element passed in.
0N/A *
0N/A * @param w a Writer
0N/A * @param root an Element
0N/A */
0N/A protected AbstractWriter(Writer w, Element root) {
0N/A this(w, root, 0, root.getEndOffset());
0N/A }
0N/A
0N/A /**
0N/A * Creates a new AbstractWriter.
0N/A * Initializes the ElementIterator with the
0N/A * element passed in.
0N/A *
0N/A * @param w a Writer
0N/A * @param root an Element
0N/A * @param pos The location in the document to fetch the
0N/A * content.
0N/A * @param len The amount to write out.
0N/A */
0N/A protected AbstractWriter(Writer w, Element root, int pos, int len) {
0N/A this.doc = root.getDocument();
0N/A it = new ElementIterator(root);
0N/A out = w;
0N/A startOffset = pos;
0N/A endOffset = pos + len;
0N/A canWrapLines = true;
0N/A }
0N/A
0N/A /**
0N/A * Returns the first offset to be output.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A public int getStartOffset() {
0N/A return startOffset;
0N/A }
0N/A
0N/A /**
0N/A * Returns the last offset to be output.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A public int getEndOffset() {
0N/A return endOffset;
0N/A }
0N/A
0N/A /**
0N/A * Fetches the ElementIterator.
0N/A *
0N/A * @return the ElementIterator.
0N/A */
0N/A protected ElementIterator getElementIterator() {
0N/A return it;
0N/A }
0N/A
0N/A /**
0N/A * Returns the Writer that is used to output the content.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected Writer getWriter() {
0N/A return out;
0N/A }
0N/A
0N/A /**
0N/A * Fetches the document.
0N/A *
0N/A * @return the Document.
0N/A */
0N/A protected Document getDocument() {
0N/A return doc;
0N/A }
0N/A
0N/A /**
0N/A * This method determines whether the current element
0N/A * is in the range specified. When no range is specified,
0N/A * the range is initialized to be the entire document.
0N/A * inRange() returns true if the range specified intersects
0N/A * with the element's range.
0N/A *
0N/A * @param next an Element.
0N/A * @return boolean that indicates whether the element
0N/A * is in the range.
0N/A */
0N/A protected boolean inRange(Element next) {
0N/A int startOffset = getStartOffset();
0N/A int endOffset = getEndOffset();
0N/A if ((next.getStartOffset() >= startOffset &&
0N/A next.getStartOffset() < endOffset) ||
0N/A (startOffset >= next.getStartOffset() &&
0N/A startOffset < next.getEndOffset())) {
0N/A return true;
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * This abstract method needs to be implemented
0N/A * by subclasses. Its responsibility is to
0N/A * iterate over the elements and use the write()
0N/A * methods to generate output in the desired format.
0N/A */
0N/A abstract protected void write() throws IOException, BadLocationException;
0N/A
0N/A /**
0N/A * Returns the text associated with the element.
0N/A * The assumption here is that the element is a
0N/A * leaf element. Throws a BadLocationException
0N/A * when encountered.
0N/A *
0N/A * @param elem an <code>Element</code>
0N/A * @exception BadLocationException if pos represents an invalid
0N/A * location within the document
0N/A * @return the text as a <code>String</code>
0N/A */
0N/A protected String getText(Element elem) throws BadLocationException {
0N/A return doc.getText(elem.getStartOffset(),
0N/A elem.getEndOffset() - elem.getStartOffset());
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Writes out text. If a range is specified when the constructor
0N/A * is invoked, then only the appropriate range of text is written
0N/A * out.
0N/A *
0N/A * @param elem an Element.
0N/A * @exception IOException on any I/O error
0N/A * @exception BadLocationException if pos represents an invalid
0N/A * location within the document.
0N/A */
0N/A protected void text(Element elem) throws BadLocationException,
0N/A IOException {
0N/A int start = Math.max(getStartOffset(), elem.getStartOffset());
0N/A int end = Math.min(getEndOffset(), elem.getEndOffset());
0N/A if (start < end) {
0N/A if (segment == null) {
0N/A segment = new Segment();
0N/A }
0N/A getDocument().getText(start, end - start, segment);
0N/A if (segment.count > 0) {
0N/A write(segment.array, segment.offset, segment.count);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Enables subclasses to set the number of characters they
0N/A * want written per line. The default is 100.
0N/A *
0N/A * @param l the maximum line length.
0N/A */
0N/A protected void setLineLength(int l) {
0N/A maxLineLength = l;
0N/A }
0N/A
0N/A /**
0N/A * Returns the maximum line length.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected int getLineLength() {
0N/A return maxLineLength;
0N/A }
0N/A
0N/A /**
0N/A * Sets the current line length.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected void setCurrentLineLength(int length) {
0N/A currLength = length;
0N/A isLineEmpty = (currLength == 0);
0N/A }
0N/A
0N/A /**
0N/A * Returns the current line length.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected int getCurrentLineLength() {
0N/A return currLength;
0N/A }
0N/A
0N/A /**
0N/A * Returns true if the current line should be considered empty. This
0N/A * is true when <code>getCurrentLineLength</code> == 0 ||
0N/A * <code>indent</code> has been invoked on an empty line.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected boolean isLineEmpty() {
0N/A return isLineEmpty;
0N/A }
0N/A
0N/A /**
0N/A * Sets whether or not lines can be wrapped. This can be toggled
0N/A * during the writing of lines. For example, outputting HTML might
0N/A * set this to false when outputting a quoted string.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected void setCanWrapLines(boolean newValue) {
0N/A canWrapLines = newValue;
0N/A }
0N/A
0N/A /**
0N/A * Returns whether or not the lines can be wrapped. If this is false
0N/A * no lineSeparator's will be output.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected boolean getCanWrapLines() {
0N/A return canWrapLines;
0N/A }
0N/A
0N/A /**
0N/A * Enables subclasses to specify how many spaces an indent
0N/A * maps to. When indentation takes place, the indent level
0N/A * is multiplied by this mapping. The default is 2.
0N/A *
0N/A * @param space an int representing the space to indent mapping.
0N/A */
0N/A protected void setIndentSpace(int space) {
0N/A indentSpace = space;
0N/A }
0N/A
0N/A /**
0N/A * Returns the amount of space to indent.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected int getIndentSpace() {
0N/A return indentSpace;
0N/A }
0N/A
0N/A /**
0N/A * Sets the String used to reprsent newlines. This is initialized
0N/A * in the constructor from either the Document, or the System property
0N/A * line.separator.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A public void setLineSeparator(String value) {
0N/A lineSeparator = value;
0N/A }
0N/A
0N/A /**
0N/A * Returns the string used to represent newlines.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A public String getLineSeparator() {
0N/A return lineSeparator;
0N/A }
0N/A
0N/A /**
0N/A * Increments the indent level. If indenting would cause
0N/A * <code>getIndentSpace()</code> *<code>getIndentLevel()</code> to be >
0N/A * than <code>getLineLength()</code> this will not cause an indent.
0N/A */
0N/A protected void incrIndent() {
0N/A // Only increment to a certain point.
0N/A if (offsetIndent > 0) {
0N/A offsetIndent++;
0N/A }
0N/A else {
0N/A if (++indentLevel * getIndentSpace() >= getLineLength()) {
0N/A offsetIndent++;
0N/A --indentLevel;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Decrements the indent level.
0N/A */
0N/A protected void decrIndent() {
0N/A if (offsetIndent > 0) {
0N/A --offsetIndent;
0N/A }
0N/A else {
0N/A indentLevel--;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the current indentation level. That is, the number of times
0N/A * <code>incrIndent</code> has been invoked minus the number of times
0N/A * <code>decrIndent</code> has been invoked.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected int getIndentLevel() {
0N/A return indentLevel;
0N/A }
0N/A
0N/A /**
0N/A * Does indentation. The number of spaces written
0N/A * out is indent level times the space to map mapping. If the current
0N/A * line is empty, this will not make it so that the current line is
0N/A * still considered empty.
0N/A *
0N/A * @exception IOException on any I/O error
0N/A */
0N/A protected void indent() throws IOException {
0N/A int max = getIndentLevel() * getIndentSpace();
0N/A if (indentChars == null || max > indentChars.length) {
0N/A indentChars = new char[max];
0N/A for (int counter = 0; counter < max; counter++) {
0N/A indentChars[counter] = ' ';
0N/A }
0N/A }
0N/A int length = getCurrentLineLength();
0N/A boolean wasEmpty = isLineEmpty();
0N/A output(indentChars, 0, max);
0N/A if (wasEmpty && length == 0) {
0N/A isLineEmpty = true;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Writes out a character. This is implemented to invoke
0N/A * the <code>write</code> method that takes a char[].
0N/A *
0N/A * @param ch a char.
0N/A * @exception IOException on any I/O error
0N/A */
0N/A protected void write(char ch) throws IOException {
0N/A if (tempChars == null) {
0N/A tempChars = new char[128];
0N/A }
0N/A tempChars[0] = ch;
0N/A write(tempChars, 0, 1);
0N/A }
0N/A
0N/A /**
0N/A * Writes out a string. This is implemented to invoke the
0N/A * <code>write</code> method that takes a char[].
0N/A *
0N/A * @param content a String.
0N/A * @exception IOException on any I/O error
0N/A */
0N/A protected void write(String content) throws IOException {
0N/A if (content == null) {
0N/A return;
0N/A }
0N/A int size = content.length();
0N/A if (tempChars == null || tempChars.length < size) {
0N/A tempChars = new char[size];
0N/A }
0N/A content.getChars(0, size, tempChars, 0);
0N/A write(tempChars, 0, size);
0N/A }
0N/A
0N/A /**
0N/A * Writes the line separator. This invokes <code>output</code> directly
0N/A * as well as setting the <code>lineLength</code> to 0.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected void writeLineSeparator() throws IOException {
0N/A String newline = getLineSeparator();
0N/A int length = newline.length();
0N/A if (newlineChars == null || newlineChars.length < length) {
0N/A newlineChars = new char[length];
0N/A }
0N/A newline.getChars(0, length, newlineChars, 0);
0N/A output(newlineChars, 0, length);
0N/A setCurrentLineLength(0);
0N/A }
0N/A
0N/A /**
0N/A * All write methods call into this one. If <code>getCanWrapLines()</code>
0N/A * returns false, this will call <code>output</code> with each sequence
0N/A * of <code>chars</code> that doesn't contain a NEWLINE, followed
0N/A * by a call to <code>writeLineSeparator</code>. On the other hand,
0N/A * if <code>getCanWrapLines()</code> returns true, this will split the
0N/A * string, as necessary, so <code>getLineLength</code> is honored.
0N/A * The only exception is if the current string contains no whitespace,
0N/A * and won't fit in which case the line length will exceed
0N/A * <code>getLineLength</code>.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected void write(char[] chars, int startIndex, int length)
0N/A throws IOException {
0N/A if (!getCanWrapLines()) {
0N/A // We can not break string, just track if a newline
0N/A // is in it.
0N/A int lastIndex = startIndex;
0N/A int endIndex = startIndex + length;
0N/A int newlineIndex = indexOf(chars, NEWLINE, startIndex, endIndex);
0N/A while (newlineIndex != -1) {
0N/A if (newlineIndex > lastIndex) {
0N/A output(chars, lastIndex, newlineIndex - lastIndex);
0N/A }
0N/A writeLineSeparator();
0N/A lastIndex = newlineIndex + 1;
0N/A newlineIndex = indexOf(chars, '\n', lastIndex, endIndex);
0N/A }
0N/A if (lastIndex < endIndex) {
0N/A output(chars, lastIndex, endIndex - lastIndex);
0N/A }
0N/A }
0N/A else {
0N/A // We can break chars if the length exceeds maxLength.
0N/A int lastIndex = startIndex;
0N/A int endIndex = startIndex + length;
0N/A int lineLength = getCurrentLineLength();
0N/A int maxLength = getLineLength();
0N/A
0N/A while (lastIndex < endIndex) {
0N/A int newlineIndex = indexOf(chars, NEWLINE, lastIndex,
0N/A endIndex);
0N/A boolean needsNewline = false;
0N/A boolean forceNewLine = false;
0N/A
0N/A lineLength = getCurrentLineLength();
0N/A if (newlineIndex != -1 && (lineLength +
0N/A (newlineIndex - lastIndex)) < maxLength) {
0N/A if (newlineIndex > lastIndex) {
0N/A output(chars, lastIndex, newlineIndex - lastIndex);
0N/A }
0N/A lastIndex = newlineIndex + 1;
0N/A forceNewLine = true;
0N/A }
0N/A else if (newlineIndex == -1 && (lineLength +
0N/A (endIndex - lastIndex)) < maxLength) {
0N/A if (endIndex > lastIndex) {
0N/A output(chars, lastIndex, endIndex - lastIndex);
0N/A }
0N/A lastIndex = endIndex;
0N/A }
0N/A else {
0N/A // Need to break chars, find a place to split chars at,
0N/A // from lastIndex to endIndex,
0N/A // or maxLength - lineLength whichever is smaller
0N/A int breakPoint = -1;
0N/A int maxBreak = Math.min(endIndex - lastIndex,
0N/A maxLength - lineLength - 1);
0N/A int counter = 0;
0N/A while (counter < maxBreak) {
0N/A if (Character.isWhitespace(chars[counter +
0N/A lastIndex])) {
0N/A breakPoint = counter;
0N/A }
0N/A counter++;
0N/A }
0N/A if (breakPoint != -1) {
0N/A // Found a place to break at.
0N/A breakPoint += lastIndex + 1;
0N/A output(chars, lastIndex, breakPoint - lastIndex);
0N/A lastIndex = breakPoint;
0N/A needsNewline = true;
0N/A }
0N/A else {
0N/A // No where good to break.
0N/A
0N/A // find the next whitespace, or write out the
0N/A // whole string.
0N/A // maxBreak will be negative if current line too
0N/A // long.
0N/A counter = Math.max(0, maxBreak);
0N/A maxBreak = endIndex - lastIndex;
0N/A while (counter < maxBreak) {
0N/A if (Character.isWhitespace(chars[counter +
0N/A lastIndex])) {
0N/A breakPoint = counter;
0N/A break;
0N/A }
0N/A counter++;
0N/A }
0N/A if (breakPoint == -1) {
0N/A output(chars, lastIndex, endIndex - lastIndex);
0N/A breakPoint = endIndex;
0N/A }
0N/A else {
0N/A breakPoint += lastIndex;
0N/A if (chars[breakPoint] == NEWLINE) {
0N/A output(chars, lastIndex, breakPoint++ -
0N/A lastIndex);
0N/A forceNewLine = true;
0N/A }
0N/A else {
0N/A output(chars, lastIndex, ++breakPoint -
0N/A lastIndex);
0N/A needsNewline = true;
0N/A }
0N/A }
0N/A lastIndex = breakPoint;
0N/A }
0N/A }
0N/A if (forceNewLine || needsNewline || lastIndex < endIndex) {
0N/A writeLineSeparator();
0N/A if (lastIndex < endIndex || !forceNewLine) {
0N/A indent();
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Writes out the set of attributes as " <name>=<value>"
0N/A * pairs. It throws an IOException when encountered.
0N/A *
0N/A * @param attr an AttributeSet.
0N/A * @exception IOException on any I/O error
0N/A */
0N/A protected void writeAttributes(AttributeSet attr) throws IOException {
0N/A
0N/A Enumeration names = attr.getAttributeNames();
0N/A while (names.hasMoreElements()) {
0N/A Object name = names.nextElement();
0N/A write(" " + name + "=" + attr.getAttribute(name));
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * The last stop in writing out content. All the write methods eventually
0N/A * make it to this method, which invokes <code>write</code> on the
0N/A * Writer.
0N/A * <p>This method also updates the line length based on
0N/A * <code>length</code>. If this is invoked to output a newline, the
0N/A * current line length will need to be reset as will no longer be
0N/A * valid. If it is up to the caller to do this. Use
0N/A * <code>writeLineSeparator</code> to write out a newline, which will
0N/A * property update the current line length.
0N/A *
0N/A * @since 1.3
0N/A */
0N/A protected void output(char[] content, int start, int length)
0N/A throws IOException {
0N/A getWriter().write(content, start, length);
0N/A setCurrentLineLength(getCurrentLineLength() + length);
0N/A }
0N/A
0N/A /**
0N/A * Support method to locate an occurence of a particular character.
0N/A */
0N/A private int indexOf(char[] chars, char sChar, int startIndex,
0N/A int endIndex) {
0N/A while(startIndex < endIndex) {
0N/A if (chars[startIndex] == sChar) {
0N/A return startIndex;
0N/A }
0N/A startIndex++;
0N/A }
0N/A return -1;
0N/A }
0N/A}