View Javadoc
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 }