1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.maven.doxia.sink.impl;
20
21 import java.io.ByteArrayOutputStream;
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import org.apache.maven.doxia.sink.Sink;
30 import org.apache.maven.doxia.sink.SinkFactory;
31
32 /**
33 * The RandomAccessSink provides the ability to create a {@link Sink} with hooks.
34 * A page can be prepared by first creating its structure and specifying the positions of these hooks.
35 * After specifying the structure, the page can be filled with content from one or more models.
36 * These hooks can prevent you to have to loop over the model multiple times to build the page as desired.
37 * @deprecated Use {@link BufferingSinkProxyFactory} instead which buffers on the (higher) Sink API level
38 * which usually is less memory intense than buffering the output stream which is done by this class.
39 * Also it doesn't require dynamically creating new sinks leveraging a {@link SinkFactory}.
40 *
41 * @author Robert Scholte
42 * @since 1.3
43 * @see BufferingSinkProxyFactory
44 */
45 @Deprecated
46 public class RandomAccessSink extends SinkWrapper {
47 private SinkFactory sinkFactory;
48
49 private String encoding;
50
51 private OutputStream coreOutputStream;
52
53 private Sink coreSink;
54
55 private List<Sink> sinks = new ArrayList<>();
56
57 private List<ByteArrayOutputStream> outputStreams = new ArrayList<>();
58
59 private Sink currentSink;
60
61 /**
62 * <p>Constructor for RandomAccessSink.</p>
63 *
64 * @param sinkFactory a {@link org.apache.maven.doxia.sink.SinkFactory} object.
65 * @param stream a {@link java.io.OutputStream} object.
66 * @throws java.io.IOException if any.
67 */
68 public RandomAccessSink(SinkFactory sinkFactory, OutputStream stream) throws IOException {
69 super(sinkFactory.createSink(stream));
70 this.sinkFactory = sinkFactory;
71 this.coreOutputStream = stream;
72 this.coreSink = getWrappedSink();
73 }
74
75 /**
76 * <p>Constructor for RandomAccessSink.</p>
77 *
78 * @param sinkFactory a {@link org.apache.maven.doxia.sink.SinkFactory} object.
79 * @param stream a {@link java.io.OutputStream} object.
80 * @param encoding a {@link java.lang.String} object.
81 * @throws java.io.IOException if any.
82 */
83 public RandomAccessSink(SinkFactory sinkFactory, OutputStream stream, String encoding) throws IOException {
84 super(sinkFactory.createSink(stream, encoding));
85 this.sinkFactory = sinkFactory;
86 this.coreOutputStream = stream;
87 this.encoding = encoding;
88 this.coreSink = getWrappedSink();
89 }
90
91 /**
92 * <p>Constructor for RandomAccessSink.</p>
93 *
94 * @param sinkFactory a {@link org.apache.maven.doxia.sink.SinkFactory} object.
95 * @param outputDirectory a {@link java.io.File} object.
96 * @param outputName a {@link java.lang.String} object.
97 * @throws java.io.IOException if any.
98 */
99 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 }