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}