001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.doxia.module.markdown; 020 021import java.io.IOException; 022import java.io.Writer; 023 024import org.apache.commons.lang3.StringUtils; 025 026/** 027 * Decorates an existing writer to additionally temporarily buffer the last two lines written. 028 * Useful to collapse subsequent new lines or blank lines by evaluating {@link #isWriterAfterBlankLine()} and {@link #isWriterAfterBlankLine()}. 029 * The buffering does not affect or defer delegation to the underlying writer, though. 030 */ 031public class LastTwoLinesBufferingWriter extends Writer { 032 033 private final Writer out; 034 private String previousLine; 035 private StringBuilder currentLine; 036 private final String lineSeparator; 037 038 public LastTwoLinesBufferingWriter(Writer out) { 039 // don't use System.lineSeparator, as overwritten in AbstractModuleTest 040 this(out, System.getProperty("line.separator")); 041 } 042 043 LastTwoLinesBufferingWriter(Writer out, String lineSeparator) { 044 super(); 045 this.out = out; 046 this.previousLine = ""; 047 this.currentLine = new StringBuilder(); 048 this.lineSeparator = lineSeparator; 049 } 050 051 public boolean isWriterAtStartOfNewLine() { 052 return currentLine.length() == 0; 053 } 054 055 public boolean isWriterAfterBlankLine() { 056 return StringUtils.isAllBlank(currentLine) && StringUtils.isAllBlank(previousLine); 057 } 058 059 @Override 060 public void write(char[] cbuf, int off, int len) throws IOException { 061 int offsetWrittenInLineBuffer = off; 062 int index = 0; 063 while (index < len) { 064 // potentially a line break... 065 if (cbuf[off + index] == '\r' || cbuf[off + index] == '\n') { 066 int lenToWrite = index + 1 - (offsetWrittenInLineBuffer - off); 067 flushLine(cbuf, offsetWrittenInLineBuffer, lenToWrite); 068 offsetWrittenInLineBuffer += lenToWrite; 069 } 070 index++; 071 } 072 flushLine(cbuf, offsetWrittenInLineBuffer, index - (offsetWrittenInLineBuffer - off)); 073 out.write(cbuf, off, len); 074 } 075 076 private void flushLine(char[] cbuf, int off, int len) { 077 this.currentLine.append(cbuf, off, len); 078 // really a line break? 079 if (currentLine.toString().endsWith(lineSeparator)) { 080 previousLine = currentLine.toString(); 081 currentLine.setLength(0); 082 } 083 } 084 085 @Override 086 public void flush() throws IOException { 087 out.flush(); 088 } 089 090 @Override 091 public void close() throws IOException { 092 out.close(); 093 } 094}