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}