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; 023import java.util.Collections; 024import java.util.LinkedList; 025import java.util.Queue; 026 027public class BufferingStackWriter extends Writer { 028 029 /** 030 * A buffer stack that holds the output when the current context requires buffering. 031 * The content of this buffer is supposed to be already escaped. 032 */ 033 private final Queue<StringBuilder> bufferStack; 034 035 private final Writer out; 036 037 public BufferingStackWriter(Writer out) { 038 this.out = out; 039 this.bufferStack = Collections.asLifoQueue(new LinkedList<>()); 040 } 041 042 @Override 043 public void write(char[] cbuf, int off, int len) throws IOException { 044 if (bufferStack.isEmpty()) { 045 out.write(cbuf, off, len); 046 } else { 047 bufferStack.element().append(cbuf, off, len); 048 } 049 } 050 051 /** 052 * Adds another buffer to the stack. The content of the current buffer is not affected, but the new content will 053 * be written to the new buffer until it is polled. 054 */ 055 public void addBuffer() { 056 StringBuilder sb = new StringBuilder(); 057 bufferStack.add(sb); 058 } 059 060 /** 061 * Retrieves the content of the current buffer without removing it from the stack. 062 * Also writing to the StringBuffer returned by this method will affect the content of the current buffer. 063 */ 064 public StringBuilder getCurrentBuffer() { 065 return bufferStack.element(); 066 } 067 068 /** 069 * Retrieves the content of the current buffer without removing it from the stack. 070 * In contrast to {@link #getCurrentBuffer()} the current buffer is cleared. 071 */ 072 public String getAndClearCurrentBuffer() { 073 String buffer = bufferStack.remove().toString(); 074 addBuffer(); 075 return buffer; 076 } 077 078 /** 079 * Remove the current buffer from the stack. The content of the current buffer is discarded. The previous buffer in the stack becomes the current buffer. 080 */ 081 public void removeBuffer() { 082 bufferStack.remove(); 083 } 084 085 @Override 086 public void flush() throws IOException { 087 // do nothing when there are buffers in the stack 088 if (bufferStack.isEmpty()) { 089 out.flush(); 090 } 091 } 092 093 @Override 094 public void close() throws IOException { 095 if (!bufferStack.isEmpty()) { 096 throw new IllegalStateException( 097 "Cannot close BufferingStackWriter while there are still buffers in the stack."); 098 } 099 out.close(); 100 } 101}