Coverage Report - net.sf.jmatchparser.util.charset.AddBOMCharset
 
Classes in this File Line Coverage Branch Coverage Complexity
AddBOMCharset
91%
11/12
33%
2/6
2,769
AddBOMCharset$Decoder
91%
33/36
62%
10/16
2,769
AddBOMCharset$Encoder
88%
30/34
50%
6/12
2,769
 
 1  
 /*
 2  
  * Copyright (c) 2010 - 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.charset;
 34  
 
 35  
 import java.nio.ByteBuffer;
 36  
 import java.nio.CharBuffer;
 37  
 import java.nio.charset.Charset;
 38  
 import java.nio.charset.CharsetDecoder;
 39  
 import java.nio.charset.CharsetEncoder;
 40  
 import java.nio.charset.CoderResult;
 41  
 import java.nio.charset.CodingErrorAction;
 42  
 import java.util.Set;
 43  
 
 44  
 class AddBOMCharset extends Charset {
 45  
 
 46  
         static final char BOM = '\uFEFF';
 47  
         public static final String SUFFIX = "-BOM";
 48  
         private final Charset base;
 49  
 
 50  
         protected AddBOMCharset(Charset base) {
 51  6
                 super(base.name() + SUFFIX, buildAliases(base.aliases()));
 52  6
                 this.base = base;
 53  6
         }
 54  
 
 55  
         private static String[] buildAliases(Set<String> aliases) {
 56  6
                 String[] result = new String[aliases.size()];
 57  6
                 int i = 0;
 58  6
                 for (String alias : aliases) {
 59  18
                         result[i] = alias + SUFFIX;
 60  18
                         i++;
 61  
                 }
 62  6
                 return result;
 63  
         }
 64  
 
 65  
         @Override
 66  
         public boolean contains(Charset cs) {
 67  0
                 return cs instanceof AddBOMCharset || base.contains(cs);
 68  
         }
 69  
 
 70  
         @Override
 71  
         public CharsetDecoder newDecoder() {
 72  12
                 return new Decoder(base.newDecoder());
 73  
         }
 74  
 
 75  
         @Override
 76  
         public CharsetEncoder newEncoder() {
 77  6
                 return new Encoder(base.newEncoder());
 78  
         }
 79  
 
 80  
         private class Decoder extends CharsetDecoder {
 81  
 
 82  
                 private final CharsetDecoder baseDecoder;
 83  12
                 private boolean bomRead = false, flushed = false;
 84  
 
 85  12
                 protected Decoder(CharsetDecoder baseDecoder) {
 86  12
                         super(AddBOMCharset.this, baseDecoder.averageCharsPerByte(), baseDecoder.maxCharsPerByte());
 87  12
                         this.baseDecoder = baseDecoder;
 88  12
                         baseDecoder.onMalformedInput(CodingErrorAction.REPORT);
 89  12
                         baseDecoder.onUnmappableCharacter(CodingErrorAction.REPORT);
 90  12
                 }
 91  
 
 92  
                 @Override
 93  
                 protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
 94  36
                         if (out.remaining() == 0)
 95  0
                                 return CoderResult.OVERFLOW;
 96  36
                         if (!bomRead) {
 97  36
                                 CharBuffer bom = CharBuffer.allocate(1);
 98  36
                                 CoderResult bomResult = baseDecoder.decode(in, bom, false);
 99  36
                                 bom.flip();
 100  36
                                 if (bom.remaining() == 1) {
 101  36
                                         char c = bom.get();
 102  36
                                         if (c != BOM)
 103  12
                                                 out.put(c);
 104  36
                                         bomRead = true;
 105  
                                 }
 106  36
                                 if (!bomResult.isOverflow()) {
 107  12
                                         return bomResult;
 108  
                                 }
 109  24
                                 bomRead = true;
 110  
                         }
 111  24
                         return baseDecoder.decode(in, out, false);
 112  
                 }
 113  
 
 114  
                 @Override
 115  
                 protected CoderResult implFlush(CharBuffer out) {
 116  30
                         if (!flushed) {
 117  30
                                 ByteBuffer empty = ByteBuffer.allocate(1);
 118  30
                                 empty.flip();
 119  30
                                 CoderResult result = baseDecoder.decode(empty, out, true);
 120  30
                                 if (!result.isUnderflow())
 121  0
                                         return result;
 122  30
                                 result = baseDecoder.flush(out);
 123  30
                                 if (!result.isUnderflow())
 124  0
                                         return result;
 125  
                         }
 126  30
                         return super.implFlush(out);
 127  
                 }
 128  
 
 129  
                 @Override
 130  
                 protected void implReset() {
 131  30
                         baseDecoder.reset();
 132  30
                         bomRead = false;
 133  30
                         flushed = false;
 134  30
                 }
 135  
         }
 136  
 
 137  
         private class Encoder extends CharsetEncoder {
 138  
 
 139  
                 private CharsetEncoder baseEncoder;
 140  6
                 boolean bomWritten = false, flushed = false;
 141  
 
 142  6
                 protected Encoder(CharsetEncoder baseEncoder) {
 143  6
                         super(AddBOMCharset.this, baseEncoder.averageBytesPerChar(), baseEncoder.maxBytesPerChar() * 2, baseEncoder.replacement());
 144  6
                         this.baseEncoder = baseEncoder;
 145  6
                         baseEncoder.onMalformedInput(CodingErrorAction.REPORT);
 146  6
                         baseEncoder.onUnmappableCharacter(CodingErrorAction.REPORT);
 147  6
                 }
 148  
 
 149  
                 @Override
 150  
                 protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
 151  24
                         if (!bomWritten) {
 152  24
                                 CharBuffer bom = CharBuffer.allocate(1);
 153  24
                                 bom.put(BOM);
 154  24
                                 bom.flip();
 155  24
                                 CoderResult result = baseEncoder.encode(bom, out, false);
 156  24
                                 if (!result.isUnderflow())
 157  0
                                         return result;
 158  24
                                 bomWritten = true;
 159  
                         }
 160  24
                         return baseEncoder.encode(in, out, false);
 161  
                 }
 162  
 
 163  
                 @Override
 164  
                 protected void implReset() {
 165  24
                         baseEncoder.reset();
 166  24
                         bomWritten = false;
 167  24
                         flushed = false;
 168  24
                 }
 169  
 
 170  
                 @Override
 171  
                 protected CoderResult implFlush(ByteBuffer out) {
 172  24
                         if (!flushed) {
 173  24
                                 CharBuffer maybeBom = CharBuffer.allocate(1);
 174  24
                                 if (!bomWritten) {
 175  0
                                         maybeBom.put(BOM);
 176  
                                 }
 177  24
                                 maybeBom.flip();
 178  24
                                 CoderResult result = baseEncoder.encode(maybeBom, out, true);
 179  24
                                 if (!result.isUnderflow())
 180  0
                                         return result;
 181  24
                                 bomWritten = true;
 182  24
                                 result = baseEncoder.flush(out);
 183  24
                                 if (!result.isUnderflow())
 184  0
                                         return result;
 185  24
                                 flushed = true;
 186  
                         }
 187  24
                         return super.implFlush(out);
 188  
                 }
 189  
         }
 190  
 }