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.sink.impl; 020 021import java.io.ByteArrayOutputStream; 022import java.io.File; 023import java.io.FileOutputStream; 024import java.io.IOException; 025import java.io.OutputStream; 026import java.util.ArrayList; 027import java.util.List; 028 029import org.apache.maven.doxia.sink.Sink; 030import org.apache.maven.doxia.sink.SinkFactory; 031 032/** 033 * The RandomAccessSink provides the ability to create a {@link Sink} with hooks. 034 * A page can be prepared by first creating its structure and specifying the positions of these hooks. 035 * After specifying the structure, the page can be filled with content from one or more models. 036 * These hooks can prevent you to have to loop over the model multiple times to build the page as desired. 037 * @deprecated Use {@link BufferingSinkProxyFactory} instead which buffers on the (higher) Sink API level 038 * which usually is less memory intense than buffering the output stream which is done by this class. 039 * Also it doesn't require dynamically creating new sinks leveraging a {@link SinkFactory}. 040 * 041 * @author Robert Scholte 042 * @since 1.3 043 * @see BufferingSinkProxyFactory 044 */ 045@Deprecated 046public class RandomAccessSink extends SinkWrapper { 047 private SinkFactory sinkFactory; 048 049 private String encoding; 050 051 private OutputStream coreOutputStream; 052 053 private Sink coreSink; 054 055 private List<Sink> sinks = new ArrayList<>(); 056 057 private List<ByteArrayOutputStream> outputStreams = new ArrayList<>(); 058 059 private Sink currentSink; 060 061 /** 062 * <p>Constructor for RandomAccessSink.</p> 063 * 064 * @param sinkFactory a {@link org.apache.maven.doxia.sink.SinkFactory} object. 065 * @param stream a {@link java.io.OutputStream} object. 066 * @throws java.io.IOException if any. 067 */ 068 public RandomAccessSink(SinkFactory sinkFactory, OutputStream stream) throws IOException { 069 super(sinkFactory.createSink(stream)); 070 this.sinkFactory = sinkFactory; 071 this.coreOutputStream = stream; 072 this.coreSink = getWrappedSink(); 073 } 074 075 /** 076 * <p>Constructor for RandomAccessSink.</p> 077 * 078 * @param sinkFactory a {@link org.apache.maven.doxia.sink.SinkFactory} object. 079 * @param stream a {@link java.io.OutputStream} object. 080 * @param encoding a {@link java.lang.String} object. 081 * @throws java.io.IOException if any. 082 */ 083 public RandomAccessSink(SinkFactory sinkFactory, OutputStream stream, String encoding) throws IOException { 084 super(sinkFactory.createSink(stream, encoding)); 085 this.sinkFactory = sinkFactory; 086 this.coreOutputStream = stream; 087 this.encoding = encoding; 088 this.coreSink = getWrappedSink(); 089 } 090 091 /** 092 * <p>Constructor for RandomAccessSink.</p> 093 * 094 * @param sinkFactory a {@link org.apache.maven.doxia.sink.SinkFactory} object. 095 * @param outputDirectory a {@link java.io.File} object. 096 * @param outputName a {@link java.lang.String} object. 097 * @throws java.io.IOException if any. 098 */ 099 public RandomAccessSink(SinkFactory sinkFactory, File outputDirectory, String outputName) throws IOException { 100 super(null); 101 this.sinkFactory = sinkFactory; 102 this.coreOutputStream = new FileOutputStream(new File(outputDirectory, outputName)); 103 this.currentSink = sinkFactory.createSink(coreOutputStream); 104 this.coreSink = this.currentSink; 105 setWrappedSink(currentSink); 106 } 107 108 /** 109 * <p>Constructor for RandomAccessSink.</p> 110 * 111 * @param sinkFactory a {@link org.apache.maven.doxia.sink.SinkFactory} object. 112 * @param outputDirectory a {@link java.io.File} object. 113 * @param outputName a {@link java.lang.String} object. 114 * @param encoding a {@link java.lang.String} object. 115 * @throws java.io.IOException if any. 116 */ 117 public RandomAccessSink(SinkFactory sinkFactory, File outputDirectory, String outputName, String encoding) 118 throws IOException { 119 super(null); 120 this.sinkFactory = sinkFactory; 121 this.coreOutputStream = new FileOutputStream(new File(outputDirectory, outputName)); 122 this.encoding = encoding; 123 this.currentSink = sinkFactory.createSink(coreOutputStream, encoding); 124 this.coreSink = this.currentSink; 125 setWrappedSink(currentSink); 126 } 127 128 /** 129 * By calling this method a sink reference is added at the current position. You can write to both the new sink 130 * reference and the original sink. After flushing all sinks will be flushed in the right order. 131 * 132 * @return a subsink reference you can write to 133 */ 134 public Sink addSinkHook() { 135 Sink subSink = null; 136 try { 137 ByteArrayOutputStream subOut = new ByteArrayOutputStream(); 138 ByteArrayOutputStream newOut = new ByteArrayOutputStream(); 139 140 outputStreams.add(subOut); 141 outputStreams.add(newOut); 142 143 if (encoding != null) { 144 subSink = sinkFactory.createSink(subOut, encoding); 145 currentSink = sinkFactory.createSink(newOut, encoding); 146 } else { 147 subSink = sinkFactory.createSink(subOut); 148 currentSink = sinkFactory.createSink(newOut); 149 } 150 sinks.add(subSink); 151 sinks.add(currentSink); 152 setWrappedSink(currentSink); 153 } catch (IOException e) { 154 // IOException can only be caused by our own ByteArrayOutputStream 155 } 156 return subSink; 157 } 158 159 /** 160 * Close all sinks 161 */ 162 public void close() { 163 for (Sink sink : sinks) { 164 // sink is responsible for closing it's stream 165 sink.close(); 166 } 167 coreSink.close(); 168 } 169 170 /** 171 * Flush all sinks 172 */ 173 public void flush() { 174 for (int i = 0; i < sinks.size(); i++) { 175 // first flush to get complete buffer 176 // sink is responsible for flushing it's stream 177 Sink sink = sinks.get(i); 178 sink.flush(); 179 180 ByteArrayOutputStream stream = outputStreams.get(i); 181 try { 182 coreOutputStream.write(stream.toByteArray()); 183 } catch (IOException e) { 184 // @todo 185 } 186 } 187 coreSink.flush(); 188 } 189}