1 52 53 package freemarker.template.utility; 54 55 import java.io.IOException ; 56 import java.io.Writer ; 57 import java.util.Map ; 58 59 import freemarker.template.TemplateBooleanModel; 60 import freemarker.template.TemplateModelException; 61 import freemarker.template.TemplateNumberModel; 62 import freemarker.template.TemplateTransformModel; 63 64 107 public class StandardCompress implements TemplateTransformModel { 108 private static final String BUFFER_SIZE_KEY = "buffer_size"; 109 private static final String SINGLE_LINE_KEY = "single_line"; 110 private int defaultBufferSize; 111 112 public static final StandardCompress INSTANCE = new StandardCompress(); 113 114 public StandardCompress() 115 { 116 this(2048); 117 } 118 119 122 public StandardCompress(int defaultBufferSize) 123 { 124 this.defaultBufferSize = defaultBufferSize; 125 } 126 127 public Writer getWriter(final Writer out, Map args) 128 throws TemplateModelException 129 { 130 int bufferSize = defaultBufferSize; 131 boolean singleLine = false; 132 if (args != null) { 133 try { 134 TemplateNumberModel num = (TemplateNumberModel)args.get(BUFFER_SIZE_KEY); 135 if (num != null) 136 bufferSize = num.getAsNumber().intValue(); 137 } catch (ClassCastException e) { 138 throw new TemplateModelException("Expecting numerical argument to " + BUFFER_SIZE_KEY); 139 } 140 try { 141 TemplateBooleanModel flag = (TemplateBooleanModel)args.get(SINGLE_LINE_KEY); 142 if (flag != null) 143 singleLine = flag.getAsBoolean(); 144 } catch (ClassCastException e) { 145 throw new TemplateModelException("Expecting boolean argument to " + SINGLE_LINE_KEY); 146 } 147 } 148 return new StandardCompressWriter(out, bufferSize, singleLine); 149 } 150 151 private static class StandardCompressWriter extends Writer 152 { 153 private static final int MAX_EOL_LENGTH = 2; 155 private static final int AT_BEGINNING = 0; 156 private static final int SINGLE_LINE = 1; 157 private static final int INIT = 2; 158 private static final int SAW_CR = 3; 159 private static final int LINEBREAK_CR = 4; 160 private static final int LINEBREAK_CRLF = 5; 161 private static final int LINEBREAK_LF = 6; 162 163 private final Writer out; 164 private final char[] buf; 165 private final boolean singleLine; 166 167 private int pos = 0; 168 private boolean inWhitespace = true; 169 private int lineBreakState = AT_BEGINNING; 170 171 public StandardCompressWriter(Writer out, int bufSize, boolean singleLine) { 172 this.out = out; 173 this.singleLine = singleLine; 174 buf = new char[bufSize]; 175 } 176 177 public void write(char[] cbuf, int off, int len) throws IOException { 178 for (;;) { 179 int room = buf.length - pos - MAX_EOL_LENGTH; 181 if (room >= len) { 182 writeHelper(cbuf, off, len); 183 break; 184 } else if (room <= 0) { 185 flushInternal(); 186 } else { 187 writeHelper(cbuf, off, room); 188 flushInternal(); 189 off += room; 190 len -= room; 191 } 192 } 193 } 194 195 private void writeHelper(char[] cbuf, int off, int len) { 196 for (int i = off, end = off + len; i < end; i++) { 197 char c = cbuf[i]; 198 if (Character.isWhitespace(c)) { 199 inWhitespace = true; 200 updateLineBreakState(c); 201 } else if (inWhitespace) { 202 inWhitespace = false; 203 writeLineBreakOrSpace(); 204 buf[pos++] = c; 205 } else { 206 buf[pos++] = c; 207 } 208 } 209 } 210 211 218 private void updateLineBreakState(char c) 219 { 220 switch (lineBreakState) { 221 case INIT: 222 if (c == '\r') { 223 lineBreakState = SAW_CR; 224 } else if (c == '\n') { 225 lineBreakState = LINEBREAK_LF; 226 } 227 break; 228 case SAW_CR: 229 if (c == '\n') { 230 lineBreakState = LINEBREAK_CRLF; 231 } else { 232 lineBreakState = LINEBREAK_CR; 233 } 234 } 235 } 236 237 private void writeLineBreakOrSpace() 238 { 239 switch (lineBreakState) { 240 case SAW_CR: 241 case LINEBREAK_CR: 243 buf[pos++] = '\r'; 244 break; 245 case LINEBREAK_CRLF: 246 buf[pos++] = '\r'; 247 case LINEBREAK_LF: 249 buf[pos++] = '\n'; 250 break; 251 case AT_BEGINNING: 252 break; 254 case INIT: 255 case SINGLE_LINE: 256 buf[pos++] = ' '; 257 } 258 lineBreakState = (singleLine) ? SINGLE_LINE : INIT; 259 } 260 261 private void flushInternal() throws IOException { 262 out.write(buf, 0, pos); 263 pos = 0; 264 } 265 266 public void flush() throws IOException { 267 flushInternal(); 268 out.flush(); 269 } 270 271 public void close() throws IOException { 272 flushInternal(); 273 } 274 } 275 } 276 | Popular Tags |