Coverage Report - net.sf.jmatchparser.util.split.SplitOutputStream
 
Classes in this File Line Coverage Branch Coverage Complexity
SplitOutputStream
80%
70/87
66%
28/42
3,909
SplitOutputStream$1
100%
1/1
N/A
3,909
SplitOutputStream$OutMode
100%
2/2
N/A
3,909
SplitOutputStream$StreamFactory
N/A
N/A
3,909
 
 1  
 /*
 2  
  * Copyright (c) 2006 - 2011 Michael Schierl
 3  
  * 
 4  
  * All rights reserved.
 5  
  * 
 6  
  * Redistribution and use in source and binary forms, with or without
 7  
  * modification, are permitted provided that the following conditions
 8  
  * are met:
 9  
  * 
 10  
  * - Redistributions of source code must retain the above copyright notice,
 11  
  *   this list of conditions and the following disclaimer.
 12  
  *   
 13  
  * - Redistributions in binary form must reproduce the above copyright
 14  
  *   notice, this list of conditions and the following disclaimer in the
 15  
  *   documentation and/or other materials provided with the distribution.
 16  
  *   
 17  
  * - Neither name of the copyright holders nor the names of its
 18  
  *   contributors may be used to endorse or promote products derived from
 19  
  *   this software without specific prior written permission.
 20  
  *   
 21  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND THE CONTRIBUTORS
 22  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 23  
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 24  
  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 25  
  * HOLDERS OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 26  
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 27  
  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 28  
  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 29  
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 30  
  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 31  
  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 32  
  */
 33  
 package net.sf.jmatchparser.util.split;
 34  
 
 35  
 import java.io.ByteArrayOutputStream;
 36  
 import java.io.IOException;
 37  
 import java.io.OutputStream;
 38  
 
 39  
 /**
 40  
  * Output stream that can split the output into multiple streams/files, and may
 41  
  * optionally add a header and/or footer to each stream. A {@link SplitRule} is
 42  
  * used to determine the split positions.
 43  
  */
 44  
 public class SplitOutputStream extends OutputStream {
 45  
 
 46  171
         private OutputStream out = null;
 47  171
         private OutMode outMode = OutMode.START;
 48  
         private final StreamFactory factory;
 49  
         private final SplitRule rule;
 50  171
         private byte[] header = null, footer = null;
 51  171
         private int index = 0;
 52  
 
 53  
         /**
 54  
          * Create a new split output stream.
 55  
          * 
 56  
          * @param factory
 57  
          *            Factory used to create streams
 58  
          * @param rule
 59  
          *            Split rule to determine when to split the stream
 60  
          */
 61  171
         public SplitOutputStream(StreamFactory factory, SplitRule rule) {
 62  171
                 this.factory = factory;
 63  171
                 this.rule = rule;
 64  171
         }
 65  
 
 66  
         @Override
 67  
         public void write(byte[] b, int off, int len) throws IOException {
 68  1958
                 if (shouldCount()) {
 69  1944
                         int splitPos = rule.findSplitPos(b, off, len);
 70  1944
                         if (splitPos == -1) {
 71  699
                                 out.write(b, off, len);
 72  699
                                 rule.update(b, off, len);
 73  1245
                         } else if (splitPos == len) {
 74  685
                                 out.write(b, off, len);
 75  685
                                 rule.update(b, off, len);
 76  685
                                 next();
 77  560
                         } else if (len == 1) {
 78  0
                                 throw new IOException("Cannot split one byte");
 79  
                         } else {
 80  
                                 // recursive, divide&conquer
 81  560
                                 write(b, off, splitPos);
 82  560
                                 write(b, off + splitPos, len - splitPos);
 83  
                         }
 84  1944
                 } else {
 85  14
                         out.write(b, off, len);
 86  
                 }
 87  1958
         }
 88  
 
 89  
         @Override
 90  
         public void write(int b) throws IOException {
 91  0
                 if (shouldCount()) {
 92  0
                         int splitPos = rule.findSplitPos((byte) b);
 93  0
                         if (splitPos == -1) {
 94  0
                                 out.write(b);
 95  0
                                 rule.update((byte) b);
 96  0
                         } else if (splitPos == 1) {
 97  0
                                 out.write(b);
 98  0
                                 rule.update((byte) b);
 99  0
                                 next();
 100  
                         } else {
 101  0
                                 throw new IOException("Cannot split one byte");
 102  
                         }
 103  0
                 } else {
 104  0
                         out.write(b);
 105  
                 }
 106  0
         }
 107  
 
 108  
         private void next() throws IOException {
 109  685
                 if (footer != null)
 110  15
                         out.write(footer);
 111  685
                 out.flush();
 112  685
                 out.close();
 113  685
                 out = null;
 114  685
                 index++;
 115  685
                 outMode = OutMode.START;
 116  685
         }
 117  
 
 118  
         private boolean shouldCount() throws IOException {
 119  1958
                 switch (outMode) {
 120  
                 case START:
 121  810
                         out = factory.createOutputStream(index);
 122  810
                         if (header != null)
 123  18
                                 out.write(header);
 124  810
                         outMode = OutMode.NORMAL;
 125  810
                         return true;
 126  
                 case NORMAL:
 127  1134
                         return true;
 128  
                 case FOOTER:
 129  
                 case HEADER:
 130  14
                         return false;
 131  
                 case CLOSED:
 132  
                 default:
 133  0
                         throw new IOException("Stream closed");
 134  
                 }
 135  
         }
 136  
 
 137  
         /**
 138  
          * Start the header block. After calling this method, all output will be
 139  
          * added to the header of each file, until {@link #finishBlock()} is called.
 140  
          */
 141  
         public void startHeaderBlock() {
 142  8
                 if (outMode != OutMode.START || header != null)
 143  1
                         throw new IllegalStateException();
 144  7
                 outMode = OutMode.HEADER;
 145  7
                 out = new ByteArrayOutputStream();
 146  7
         }
 147  
 
 148  
         /**
 149  
          * Start the footer block. After calling this method, all output will be
 150  
          * added to the footer of each file, until {@link #finishBlock()} is called.
 151  
          */
 152  
         public void startFooterBlock() {
 153  7
                 if (outMode != OutMode.START || footer != null)
 154  0
                         throw new IllegalStateException();
 155  7
                 outMode = OutMode.FOOTER;
 156  7
                 out = new ByteArrayOutputStream();
 157  7
         }
 158  
 
 159  
         /**
 160  
          * Finish a header or footer block.
 161  
          * 
 162  
          * @see #startHeaderBlock()
 163  
          * @see #startFooterBlock()
 164  
          */
 165  
         public void finishBlock() throws IOException {
 166  14
                 if (outMode == OutMode.HEADER) {
 167  7
                         out.flush();
 168  7
                         out.close();
 169  7
                         header = ((ByteArrayOutputStream) out).toByteArray();
 170  7
                 } else if (outMode == OutMode.FOOTER) {
 171  7
                         out.flush();
 172  7
                         out.close();
 173  7
                         footer = ((ByteArrayOutputStream) out).toByteArray();
 174  
                 } else {
 175  0
                         throw new IllegalStateException();
 176  
                 }
 177  14
                 outMode = OutMode.START;
 178  14
                 out = null;
 179  14
         }
 180  
 
 181  
         @Override
 182  
         public void flush() throws IOException {
 183  170
                 if (out != null)
 184  125
                         out.flush();
 185  170
         }
 186  
 
 187  
         @Override
 188  
         public void close() throws IOException {
 189  170
                 flush();
 190  170
                 if (out != null) {
 191  125
                         if (footer != null)
 192  3
                                 out.write(footer);
 193  125
                         out.flush();
 194  125
                         out.close();
 195  
                 }
 196  170
                 outMode = OutMode.CLOSED;
 197  170
         }
 198  
 
 199  
         /**
 200  
          * Factory used to create new streams whenever the stream has to be split.
 201  
          */
 202  
         public static interface StreamFactory {
 203  
 
 204  
                 /**
 205  
                  * Create a new stream.
 206  
                  * 
 207  
                  * @param index
 208  
                  *            Zero-based index of the stream
 209  
                  */
 210  
                 public OutputStream createOutputStream(int index) throws IOException;
 211  
         }
 212  
 
 213  7
         private static enum OutMode {
 214  1
                 START, HEADER, FOOTER, NORMAL, CLOSED;
 215  
         }
 216  
 }