    001// Copyright 2007 by Basil Vandegriend.  All rights reserved.
    002package com.basilv.envgen;
    003
    004import java.io.BufferedReader;
    005import java.io.IOException;
    006import java.io.StringReader;
    007import java.io.Writer;
    008import java.util.Map;
    009
    010import freemarker.template.TemplateModelException;
    011import freemarker.template.TemplateTransformModel;
    012
    013/**
    014 * Template to format input into a form acceptable for mainframes.
    015 * The formatting changes that are made are:
    016 * <ul>
    017 * <li>Convert tabs to spaces (4 spaces per tab)</li>
    018 * <li>Trim whitespace from end of line</li>
    019 * <li>Fail with error message if a line is longer than 75 characters</li>
    020 * </ul>
    021 */
    022public class MainframeFileFormatTransform implements TemplateTransformModel {
    023// TODO: Change to TemplateDirectiveModel and update documentation to reference directives rather than transforms?
    024/* From FreeMarker release notes.
    025A new model interface, TemplateDirectiveModel provides an easier paradigm for implementing user-defined directives than TemplateTransformModel did previously. TemplateTransformModel will be deprecated.
    026
    027
    028
    029The "transform" term is now removed from the documentation. Instead the more general "user-defined directive" term is used, which encompasses macros, TemplateTransformModel-s and the new TemplateDirectiveModel-s, which are just different ways of implementing user-defined directives.
    030
    031
    032 */
    033        static final int MAX_LINE_LENGTH = 75;
    034
    035        /**
    036         * Must have a default no-arg constructor.
    037         */
    038    public MainframeFileFormatTransform() {
    039    }
    040
    041
    042    public Writer getWriter(final Writer out, Map args) throws TemplateModelException {
    043
    044        // If you want to handle arguments to the transform, this is where it is done.
    045        // Any variables must be stored in the writer instance below and not in the main transform class.
    046
    047        final StringBuffer buffer = new StringBuffer();
    048                return new Writer() {
    049                        public void write(char cbuf[], int off, int len) {
    050                                buffer.append(cbuf, off, len);
    051                        }
    052
    053                        public void flush() throws IOException {
    054                                out.flush();
    055                        }
    056
    057                        public void close() throws IOException {
    058                                StringBuffer transformedBuffer = transform(buffer);
    059                                out.write(transformedBuffer.toString());
    060                                out.flush();
    061                        }
    062
    063                };
    064    }
    065
    066    // Non-private for testing
    067        static StringBuffer transform(StringBuffer inputBuffer) throws IOException {
    068                BufferedReader reader = new BufferedReader(new StringReader(inputBuffer.toString()));
    069                StringBuffer outputBuffer = new StringBuffer(inputBuffer.length());
    070                try {
    071                        int lineNumber = 0;
    072                        String line = reader.readLine();
    073                        while (line != null) {
    074                                lineNumber++;
    075
    076                                String transformedLine = transformLine(line, lineNumber);
    077                                outputBuffer.append(transformedLine);
    078                                outputBuffer.append("\n"); // since removed by readLine().
    079
    080                                line = reader.readLine();
    081                        }
    082
    083                } finally {
    084                        reader.close();
    085                }
    086
    087                return outputBuffer;
    088        }
    089
    090
    091        // Non-private for testing.
    092    static String transformLine(String line, int lineNumber) {
    093
    094                if (line.length() == 0) {
    095                        return line;
    096                }
    097
    098                // Convert tabs to spaces
    099                String newLine = line.replaceAll("\t", "    ");
    100
    101        // Trim ending whitespace
    102        int lastNonWhitespaceIndex = newLine.length() - 1;
    103        while (Character.isWhitespace(newLine.charAt(lastNonWhitespaceIndex))) {
    104                lastNonWhitespaceIndex --;
    105                if (lastNonWhitespaceIndex < 0) {
    106                        // Line is nothing but whitespace
    107                        return "";
    108                }
    109        }
    110        newLine = newLine.substring(0, lastNonWhitespaceIndex+1);
    111
    112        if (newLine.length() > MAX_LINE_LENGTH) {
    113                throw new RuntimeException("Line #" + lineNumber +
    114                                " is longer than the maximum of " + MAX_LINE_LENGTH +
    115                                " characters. Line content = [" + line + "].");
    116        }
    117
    118        return newLine;
    119    }
    120
    121}