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}