001/* 002 * jDTAUS Banking Charset Providers 003 * Copyright (C) 2005 Christian Schulte 004 * <cs@schulte.it> 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License, or any later version. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 019 * 020 */ 021package org.jdtaus.banking.charsets.spi; 022 023import java.nio.ByteBuffer; 024import java.nio.CharBuffer; 025import java.nio.charset.Charset; 026import java.nio.charset.CharsetDecoder; 027import java.nio.charset.CharsetEncoder; 028import java.nio.charset.CoderResult; 029import java.nio.charset.CodingErrorAction; 030import java.nio.charset.spi.CharsetProvider; 031import java.util.Iterator; 032import java.util.NoSuchElementException; 033 034/** 035 * {@code CharsetProvider} for DIN-66003 Charset. 036 * <p> 037 * Name: DIN_66003<br> 038 * MIBenum: 24<br> 039 * Source: ECMA registry<br> 040 * Alias: iso-ir-21<br> 041 * Alias: de<br> 042 * Alias: ISO646-DE<br> 043 * Alias: csISO21German<br> 044 * See: RFC1345, KXS2 045 * 046 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 047 * @version $JDTAUS: DIN66003CharsetProvider.java 8661 2012-09-27 11:29:58Z schulte $ 048 */ 049public class DIN66003CharsetProvider extends CharsetProvider 050{ 051 052 /** Common name. */ 053 static final String COMMON_NAME = "DIN_66003"; 054 055 /** Alias names. */ 056 static final String[] ALIAS_NAMES = 057 { 058 "iso-ir-21", "de", "iso646-de", "csiso21german" 059 }; 060 061 /** Supported character set names. */ 062 static final String[] SUPPORTED_NAMES = 063 { 064 COMMON_NAME.toLowerCase(), "iso-ir-21", "de", "iso646-de", "csiso21german" 065 }; 066 067 static final char[] BYTE_TO_CHAR = new char[ 0xFF ]; 068 069 static final byte[] CHAR_TO_BYTE = new byte[ 0xFF ]; 070 071 static 072 { 073 for ( int i = 0x7F; i >= 0; i-- ) 074 { 075 CHAR_TO_BYTE[i] = (byte) i; 076 BYTE_TO_CHAR[i] = (char) i; 077 } 078 079 CHAR_TO_BYTE['\u00A7'] = (byte) 0x40; 080 CHAR_TO_BYTE['\u00C4'] = (byte) 0x5B; 081 CHAR_TO_BYTE['\u00D6'] = (byte) 0x5C; 082 CHAR_TO_BYTE['\u00DC'] = (byte) 0x5D; 083 CHAR_TO_BYTE['\u00E4'] = (byte) 0x7B; 084 CHAR_TO_BYTE['\u00F6'] = (byte) 0x7C; 085 CHAR_TO_BYTE['\u00FC'] = (byte) 0x7D; 086 CHAR_TO_BYTE['\u00DF'] = (byte) 0x7E; 087 088 BYTE_TO_CHAR[0x40] = '\u00A7'; 089 BYTE_TO_CHAR[0x5B] = '\u00C4'; 090 BYTE_TO_CHAR[0x5C] = '\u00D6'; 091 BYTE_TO_CHAR[0x5D] = '\u00DC'; 092 BYTE_TO_CHAR[0x7B] = '\u00E4'; 093 BYTE_TO_CHAR[0x7C] = '\u00F6'; 094 BYTE_TO_CHAR[0x7D] = '\u00FC'; 095 BYTE_TO_CHAR[0x7E] = '\u00DF'; 096 } 097 098 /** Creates a new {@code DIN66003CharsetProvider} instance. */ 099 public DIN66003CharsetProvider() 100 { 101 super(); 102 } 103 104 public Charset charsetForName( final String charsetName ) 105 { 106 Charset ret = null; 107 108 if ( charsetName != null ) 109 { 110 final String lower = charsetName.toLowerCase(); 111 for ( int i = 0; i < SUPPORTED_NAMES.length; i++ ) 112 { 113 if ( SUPPORTED_NAMES[i].equals( lower ) ) 114 { 115 ret = new DIN66003Charset(); 116 break; 117 } 118 } 119 } 120 121 return ret; 122 } 123 124 public Iterator charsets() 125 { 126 return new Iterator() 127 { 128 129 private boolean hasNext = true; 130 131 public boolean hasNext() 132 { 133 return this.hasNext; 134 } 135 136 public Object next() 137 { 138 if ( this.hasNext ) 139 { 140 this.hasNext = false; 141 return new DIN66003Charset(); 142 } 143 else 144 { 145 throw new NoSuchElementException(); 146 } 147 } 148 149 public void remove() 150 { 151 throw new UnsupportedOperationException(); 152 } 153 154 }; 155 } 156 157} 158 159/** DIN-66003 {@code Charset} implementation. */ 160class DIN66003Charset extends Charset 161{ 162 163 public DIN66003Charset() 164 { 165 super( DIN66003CharsetProvider.COMMON_NAME, DIN66003CharsetProvider.ALIAS_NAMES ); 166 } 167 168 public CharsetEncoder newEncoder() 169 { 170 return new DIN66003CharsetEncoder( this ); 171 } 172 173 public CharsetDecoder newDecoder() 174 { 175 return new DIN66003CharsetDecoder( this ); 176 } 177 178 public boolean contains( final Charset charset ) 179 { 180 return false; 181 } 182 183 static boolean isCharacterSupported( final char c ) 184 { 185 return ( c >= 0x00 && c <= 0x3F ) || ( c >= 0x41 && c <= 0x5A ) || ( c >= 0x5F && c <= 0x7A ) || 186 c == '\u00A7' || c == '\u00C4' || c == '\u00D6' || c == '\u00DC' || c == '\u00E4' || c == '\u00F6' || 187 c == '\u00FC' || c == '\u00DF'; 188 189 } 190 191} 192 193class DIN66003CharsetEncoder extends CharsetEncoder 194{ 195 196 private final char[] charBuf = new char[ 65536 ]; 197 198 DIN66003CharsetEncoder( final Charset charset ) 199 { 200 super( charset, 1f, 1f ); 201 this.onUnmappableCharacter( CodingErrorAction.REPLACE ); 202 } 203 204 protected CoderResult encodeLoop( final CharBuffer in, final ByteBuffer buf ) 205 { 206 if ( in.hasArray() && buf.hasArray() ) 207 { 208 return encodeLoopArray( in, buf ); 209 } 210 211 while ( in.hasRemaining() ) 212 { 213 in.mark(); 214 215 final int len; 216 if ( in.remaining() < this.charBuf.length ) 217 { 218 len = in.remaining(); 219 in.get( this.charBuf, 0, in.remaining() ); 220 } 221 else 222 { 223 in.get( this.charBuf, 0, this.charBuf.length ); 224 len = this.charBuf.length; 225 } 226 227 for ( int i = 0; i < len; i++ ) 228 { 229 if ( !buf.hasRemaining() ) 230 { 231 in.reset(); 232 in.position( in.position() + i ); 233 return CoderResult.OVERFLOW; 234 } 235 236 if ( !DIN66003Charset.isCharacterSupported( this.charBuf[i] ) ) 237 { 238 in.reset(); 239 in.position( in.position() + i ); 240 return CoderResult.unmappableForLength( 1 ); 241 } 242 243 buf.put( DIN66003CharsetProvider.CHAR_TO_BYTE[this.charBuf[i]] ); 244 } 245 } 246 247 return CoderResult.UNDERFLOW; 248 } 249 250 private static CoderResult encodeLoopArray( final CharBuffer in, final ByteBuffer buf ) 251 { 252 final int len = in.remaining(); 253 for ( int i = 0; i < len; i++, in.position( in.position() + 1 ), buf.position( buf.position() + 1 ) ) 254 { 255 if ( !buf.hasRemaining() ) 256 { 257 return CoderResult.OVERFLOW; 258 } 259 260 if ( !DIN66003Charset.isCharacterSupported( in.array()[in.position() + in.arrayOffset()] ) ) 261 { 262 return CoderResult.unmappableForLength( 1 ); 263 } 264 265 buf.array()[buf.position() + buf.arrayOffset()] = 266 DIN66003CharsetProvider.CHAR_TO_BYTE[in.array()[in.position() + in.arrayOffset()]]; 267 268 } 269 270 return CoderResult.UNDERFLOW; 271 } 272 273} 274 275class DIN66003CharsetDecoder extends CharsetDecoder 276{ 277 278 private final byte[] byteBuf = new byte[ 65536 ]; 279 280 DIN66003CharsetDecoder( final Charset charset ) 281 { 282 super( charset, 1f, 1f ); 283 this.onUnmappableCharacter( CodingErrorAction.REPLACE ); 284 } 285 286 protected CoderResult decodeLoop( final ByteBuffer in, final CharBuffer buf ) 287 { 288 if ( in.hasArray() && buf.hasArray() ) 289 { 290 return decodeLoopArray( in, buf ); 291 } 292 293 while ( in.hasRemaining() ) 294 { 295 in.mark(); 296 297 final int len; 298 if ( in.remaining() < this.byteBuf.length ) 299 { 300 len = in.remaining(); 301 in.get( this.byteBuf, 0, in.remaining() ); 302 } 303 else 304 { 305 in.get( this.byteBuf, 0, this.byteBuf.length ); 306 len = this.byteBuf.length; 307 } 308 309 for ( int i = 0; i < len; i++ ) 310 { 311 if ( !buf.hasRemaining() ) 312 { 313 in.reset(); 314 in.position( in.position() + i ); 315 return CoderResult.OVERFLOW; 316 } 317 318 if ( ( this.byteBuf[i] & 0xFF ) < 0x00 || ( this.byteBuf[i] & 0xFF ) > 0x7F ) 319 { 320 in.reset(); 321 in.position( in.position() + i ); 322 return CoderResult.unmappableForLength( 1 ); 323 } 324 325 buf.put( DIN66003CharsetProvider.BYTE_TO_CHAR[this.byteBuf[i] & 0xFF] ); 326 } 327 } 328 329 return CoderResult.UNDERFLOW; 330 } 331 332 private static CoderResult decodeLoopArray( final ByteBuffer in, final CharBuffer buf ) 333 { 334 final int len = in.remaining(); 335 for ( int i = 0; i < len; i++, in.position( in.position() + 1 ), buf.position( buf.position() + 1 ) ) 336 { 337 if ( !buf.hasRemaining() ) 338 { 339 return CoderResult.OVERFLOW; 340 } 341 342 if ( ( in.array()[in.position() + in.arrayOffset()] & 0xFF ) < 0x00 || 343 ( in.array()[in.position() + in.arrayOffset()] & 0xFF ) > 0x7F ) 344 { 345 return CoderResult.unmappableForLength( 1 ); 346 } 347 348 buf.array()[buf.position() + buf.arrayOffset()] = 349 DIN66003CharsetProvider.BYTE_TO_CHAR[in.array()[in.position() + in.arrayOffset()] & 0xFF]; 350 351 } 352 353 return CoderResult.UNDERFLOW; 354 } 355 356}