au.org.ala.delta.translation.PrintFile.java Source code

Java tutorial

Introduction

Here is the source code for au.org.ala.delta.translation.PrintFile.java

Source

/*******************************************************************************
 * Copyright (C) 2011 Atlas of Living Australia
 * All Rights Reserved.
 * 
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 ******************************************************************************/
package au.org.ala.delta.translation;

import au.org.ala.delta.translation.Words.Word;
import au.org.ala.delta.util.Utils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

/**
 * The PrintFile is a wrapper around an output steam that provides some utility
 * methods for line wrapping and formatting the output.
 */
public class PrintFile {

    public static final int DEFAULT_PRINT_WIDTH = 80;

    private int _printWidth = DEFAULT_PRINT_WIDTH;
    /**
     * The number of lines output on each page of the print file. A value of 0
     * indicates that no paging should be done.
     */
    private int _pageLength = 0;
    /**
     * A count of the number of lines in the current page of the print file content. 
     */
    private int _linesInCurrentPage = 0;
    private PrintStream _output;
    private int _paragraphIndent;
    private int _lineWrapIndent = 0;
    private boolean _useParagraphIndentOnLineWrap = false;
    private boolean _capitalise;

    private StringBuilder _outputBuffer;
    private boolean _indented;
    private boolean _indentOnLineWrap;
    private boolean _softWrap;
    private boolean _newFile;
    private String _newFileHeader;
    private String _fileFooter;
    private boolean _omitNextTrailingSpace = false;
    private char[] _wrapAsGroupChar;
    private boolean _trim;
    private boolean _trimLeadingSpacesOnLineWrap;
    private boolean _outputFixedWidth;

    public PrintFile(final StringBuilder buffer) {

        _output = new PrintStream(new OutputStream() {
            @Override
            public void write(int b) throws IOException {
                buffer.append((char) b);
            }
        });
        initialise();
    }

    /**
     * Creates a new Printer that will print to the supplied PrintStream.
     * 
     * @param output
     *            the output stream to print to.
     * @param lineWidth
     *            the position at which line wrapping should occur. a lineWidth
     *            of zero means no line wrapping.
     */
    public PrintFile(PrintStream output, int lineWidth) {
        _output = output;
        _printWidth = lineWidth;
        initialise();
    }

    /**
     * Creates a new Printer that will print to the supplied PrintStream.
     * 
     * @param output
     *            the output stream to print to.
     * @param lineWidth
     *            the position at which line wrapping should occur. a lineWidth
     *            of zero means no line wrapping.
     */
    public PrintFile(PrintStream output, int lineWidth, int pageLength) {
        this(output, lineWidth);
        _pageLength = pageLength;
    }

    private void initialise() {
        _outputBuffer = new StringBuilder();
        _indented = false;

        _indentOnLineWrap = false;
        _softWrap = false;
        _newFile = true;
        _newFileHeader = "";
        _trim = true;
        _outputFixedWidth = false;
        _trimLeadingSpacesOnLineWrap = false;
    }

    public void setSoftWrap(boolean softWrap) {
        _softWrap = softWrap;
    }

    public void setWrapingGroupChars(char startGroup, char endGroup) {
        _wrapAsGroupChar = new char[] { startGroup, endGroup };
    }

    public void setTrimInput(boolean trim) {
        setTrimInput(trim, false);
    }

    /**
     * Sets the trimming mode for text printed by this object. The default is
     * true.
     * 
     * @param trim
     *            If trim is false, input will not be trimmed before being
     *            output.
     * @param trimLeadingOnLineWrap
     *            If trimLeadingOnLineWrap is true, the leading spaces will be
     *            removed when a line of text is wrapped. This parameter only
     *            has an effect if trim is false.
     */
    public void setTrimInput(boolean trim, boolean trimLeadingOnLineWrap) {
        _trim = trim;
        _trimLeadingSpacesOnLineWrap = trimLeadingOnLineWrap;
    }

    public void setOutputFixedWidth(boolean outputFixedWidth) {
        _outputFixedWidth = outputFixedWidth;
    }

    public void close() {
        _output.flush();
        if (_output != System.out && _output != System.err) {
            _output.close();
        }
    }

    /**
     * If the indentOnLineWrap property is set to true if a statement is
     * automatically wrapped, its continuation will be indented.
     */
    public void setIndentOnLineWrap(boolean indent) {
        _indentOnLineWrap = indent;
    }

    public void setIndent(int numSpaces) {

        _paragraphIndent = numSpaces;
    }

    public void indent() {
        indent(_paragraphIndent);
    }

    /**
     * Indents the default amount for the current output type. (takes a luntype
     * and a number of spaces)
     */
    public void indent(int indent) {

        if (_indented) {
            return;
        }
        writeFileHeader();
        if (_paragraphIndent <= (Math.abs(_printWidth) - 20)) {
            for (int i = 0; i < _paragraphIndent; i++) {
                _outputBuffer.append(' ');
            }
        }
        _indented = true;
    }

    public void endLine() {
        printBufferLine(false);
    }

    public void writeBlankLines(int numLines, int requiredNumLinesLeftOnPage) {
        writeFileHeader();

        if (_outputBuffer.length() > 0) {
            printBufferLine();
        }
        for (int i = 0; i < numLines; i++) {
            _output.println();
            handleNewPageLine();
        }
    }

    public void printBufferLine() {
        printBufferLine(false);
    }

    public void printBufferLine(boolean indentNewLine) {

        int i = _outputBuffer.length() - 1;
        if (_trim) {
            while (i > 0 && _outputBuffer.charAt(i) == ' ') {
                i--;
            }
        }
        if (_outputBuffer.length() > 0) {

            println(_outputBuffer.substring(0, i + 1));
            _indented = false;
            _outputBuffer = new StringBuilder();

            if (indentNewLine) {
                int lineWrap = _lineWrapIndent;
                if (_useParagraphIndentOnLineWrap) {
                    lineWrap = _paragraphIndent;
                }
                for (int j = 0; j < lineWrap; j++) {
                    _outputBuffer.append(' ');
                }
            }
        }
    }

    protected void println(String text) {

        if (_outputFixedWidth) {
            text = pad(text);
        }

        _output.println(text);
        _output.flush();
        handleNewPageLine();
    }

    private void handleNewPageLine() {
        _linesInCurrentPage++;
        if (_pageLength > 0 && _linesInCurrentPage == _pageLength) {
            _output.println("\f");
            _output.flush();
            _linesInCurrentPage = 0;
        }
    }

    protected String pad(String value) {
        StringBuilder paddedValue = new StringBuilder(value);
        while (paddedValue.length() % _printWidth != 0) {
            paddedValue.append(' ');
        }
        return paddedValue.toString();
    }

    public void writeJustifiedText(String text, int completionAction) {
        writeFileHeader();
        if (_trim) {
            text = text.trim();
        }
        writeJustifiedText(text, completionAction, true);
    }

    private void writeJustifiedText(String text, int completionAction, boolean addSpaceIfRequired) {
        text = doSubstitutions(text);

        if (_capitalise) {
            text = capitaliseFirstWord(text);
        }

        if (needsLineWrap() == false) {
            printBufferLine(_indentOnLineWrap);
        }

        // Insert a space if one is required.
        if (addSpaceIfRequired && !text.startsWith(" ")) {
            insertTrailingSpace();
        }

        _outputBuffer.append(text);

        while (needsLineWrap() == false) {

            int wrappingPos = findWrapPosition();

            String trailingText = _outputBuffer.substring(wrappingPos);
            _outputBuffer.delete(wrappingPos, _outputBuffer.length());
            printBufferLine(_indentOnLineWrap);

            if (_trim) {
                trailingText = trailingText.trim();
            } else if (_trimLeadingSpacesOnLineWrap) {
                trailingText = StringUtils.stripStart(trailingText, null);
            }
            _outputBuffer.append(trailingText);
        }
        complete(completionAction);
    }

    private String doSubstitutions(String text) {
        return KeywordSubstitutions.substitute(text);
    }

    private int findWrapPosition() {
        int wrappingPos = -1;
        int newLinePos = _outputBuffer.indexOf("\n");
        if (newLinePos >= 0 && newLinePos <= _printWidth) {
            wrappingPos = newLinePos;
            _outputBuffer.deleteCharAt(newLinePos);
            if (newLinePos > 0 && _outputBuffer.charAt(newLinePos - 1) == '\r') {
                _outputBuffer.deleteCharAt(newLinePos - 1);
                wrappingPos--;
            }
        } else {
            int numSpaces = numLeadingSpaces(_outputBuffer);
            wrappingPos = findWrappingSpace();
            if (wrappingPos <= numSpaces) {
                if (_softWrap) {
                    wrappingPos = _outputBuffer.indexOf(" ", _printWidth);
                    if (wrappingPos < 0) {
                        wrappingPos = _outputBuffer.length();
                    }
                } else {
                    wrappingPos = _printWidth;
                }
            }
        }

        return wrappingPos;
    }

    private int findWrappingSpace() {
        int wrappingPos;
        if (_wrapAsGroupChar == null) {
            wrappingPos = _outputBuffer.lastIndexOf(" ", _printWidth);
        } else {
            int maxSpace = 0;
            int groupNest = 0;
            int maxGroupStart = 0;
            for (int i = 0; i < _printWidth; i++) {
                if (_wrapAsGroupChar[0] == _wrapAsGroupChar[1]) {
                    if (_outputBuffer.charAt(i) == _wrapAsGroupChar[0]) {
                        groupNest = groupNest == 0 ? 1 : 0;
                    }
                } else {
                    if (_outputBuffer.charAt(i) == _wrapAsGroupChar[0]) {
                        groupNest++;
                        maxGroupStart = i;
                    } else if (_outputBuffer.charAt(i) == _wrapAsGroupChar[1]) {
                        groupNest = Math.max(groupNest - 1, 0);

                    }
                }
                if (_outputBuffer.charAt(i) == ' ') {
                    if (groupNest == 0) {
                        maxSpace = i;
                    }
                }
            }
            if (groupNest > 0 && maxSpace == 0) {
                wrappingPos = maxGroupStart;
            } else if (maxSpace > 0) {
                wrappingPos = maxSpace + 1;
            } else {
                wrappingPos = _printWidth;
            }

        }
        return wrappingPos;
    }

    public void writeTypeSettingMark(String mark) {
        writeFileHeader();
        boolean tmpCapitalise = _capitalise;
        _capitalise = false;
        if (!spaceRequired()) {
            _omitNextTrailingSpace = true;
        }
        writeJustifiedText(mark, -1, false);
        _capitalise = tmpCapitalise;
    }

    private void insertTrailingSpace() {
        if (_omitNextTrailingSpace) {
            _omitNextTrailingSpace = false;
            return;
        }
        if (spaceRequired()) {
            _outputBuffer.append(' ');
        }
    }

    private boolean spaceRequired() {
        return (_outputBuffer.length() > 0 && lastCharInBuffer() != ' ');
    }

    private void complete(int completionAction) {
        if ((completionAction == 0) && (needsLineWrap())) {
            _outputBuffer.append(' ');
        } else if (completionAction > 0) {
            writeBlankLines(completionAction, 0);
        }
    }

    /**
     * Returns the number of leading spaces in the supplied text.
     * 
     * @param text
     *            the text to count leading spaces of.
     * @return the number of leading spaces in the supplied text or zero if the
     *         parameter is null.
     */
    private int numLeadingSpaces(CharSequence text) {
        if ((text == null) || (text.length() == 0)) {
            return 0;
        }
        int numSpaces = 0;
        while (text.charAt(numSpaces) == ' ') {
            numSpaces++;
        }
        return numSpaces;
    }

    /**
     * Capitalises the first word in the supplied text (which may contain RTF
     * markup) the first letter of the word is preceded by a '|'.
     * 
     * @param text
     *            the text to capitalise.
     * @return the text with the first word capitalised.
     */
    public String capitaliseFirstWord(String text) {
        if (StringUtils.isEmpty(text)) {
            return text;
        }

        _capitalise = false;
        return Utils.capitaliseFirstWord(text);
    }

    protected void newLine() {
        _output.println();
        handleNewPageLine();
    }

    public void newParagraph() {
        writeFileHeader();
        newLine();
        indent();
    }

    private int bufferIndex() {
        return _outputBuffer.length() - 1;
    }

    private boolean needsLineWrap() {
        if (_printWidth == 0) {
            return true;
        }
        int newLinePos = _outputBuffer.indexOf("\n");
        return bufferIndex() < Math.abs(_printWidth) && (newLinePos == -1);
    }

    private char lastCharInBuffer() {
        if (_outputBuffer.length() == 0) {
            return 0;
        }
        return _outputBuffer.charAt(_outputBuffer.length() - 1);
    }

    public void capitaliseNextWord() {
        _capitalise = true;

    }

    public void insertPunctuationMark(Word word) {

        String punctuationMark = Words.word(word);
        assert punctuationMark.length() == 1;

        if (lastCharInBuffer() != punctuationMark.charAt(0)) {
            writeFromVocabulary(word, -1);
        }
    }

    public void writeFromVocabulary(Word word, int completionAction) {
        writeJustifiedText(Words.word(word), completionAction, false);
    }

    public void outputLine(int indent, String value, int numTrailingBlanks) {
        setIndent(indent);
        outputLine(value, numTrailingBlanks);
    }

    public void outputLine(String value) {
        outputLine(value, 0);
    }

    private void outputLine(String value, int numTrailingBlanks) {
        indent();
        writeJustifiedText(value, -1);
        printBufferLine();
        if (numTrailingBlanks > 0) {
            writeBlankLines(numTrailingBlanks, 0);
        }
    }

    /**
     * Output a pair of strings, separated by multiple instances of a supplied
     * padding character. The padding character is used to ensure that the
     * content fills the print width exactly.
     * 
     * E.g. str1.........................str2
     * 
     * @param str1
     *            The first string
     * @param str2
     *            The second string
     * @param paddingChar
     *            the padding character
     */
    public void outputStringPairWithPaddingCharacter(String str1, String str2, char paddingChar) {
        indent();
        writeJustifiedText(str1, -1);

        int currentLineLength = _outputBuffer.length();
        if (currentLineLength + str2.length() >= _printWidth) {
            _outputBuffer
                    .append(StringUtils.repeat(Character.toString(paddingChar), _printWidth - currentLineLength));
            printBufferLine(_indentOnLineWrap);
            currentLineLength = _outputBuffer.length();
            _outputBuffer.append(StringUtils.repeat(Character.toString(paddingChar),
                    _printWidth - currentLineLength - str2.length()));
            _outputBuffer.append(str2);
            printBufferLine();
        } else {
            _outputBuffer.append(StringUtils.repeat(Character.toString(paddingChar),
                    _printWidth - currentLineLength - str2.length()));
            _outputBuffer.append(str2);
            printBufferLine();
        }
    }

    /**
     * If a line is wrapped during output, the line wrap indent will be applied
     * if the setIndentOnLineWrap(true) method has been invoked.
     * 
     * @param indent
     *            the indent to apply in addition to the paragraph indent if a
     *            line is wrapped.
     */
    public void setLineWrapIndent(int indent) {
        _lineWrapIndent = indent;
    }

    public void setUseParagraphIndentOnLineWrap(boolean b) {
        _useParagraphIndentOnLineWrap = b;
    }

    public void setNewFileHeader(String header) {
        _newFileHeader = header;
    }

    private void writeFileHeader() {
        if (_newFile) {
            if (StringUtils.isNotBlank(_newFileHeader)) {
                writeJustifiedText(_newFileHeader, -1, false);
                _omitNextTrailingSpace = true;
            }
            _newFile = false;
        }
    }

    public void setFileFooter(String footer) {
        _fileFooter = footer;
    }

    private void writeFooter() {
        if (StringUtils.isNotBlank(_fileFooter)) {
            outputLine(_fileFooter);
        }
    }

    public void setPrintWidth(int printWidth) {
        _printWidth = printWidth;
    }

    public void setPageLength(int pageLength) {
        _pageLength = pageLength;
    }

    public void setPrintStream(PrintStream stream) {
        // This can occur if we are changing files.

        _output = stream;
    }

    public void closePrintStream() {
        if (_output != null) {
            printBufferLine();
            writeFooter();
            IOUtils.closeQuietly(_output);
            _output = null;
        }
    }

}