View Javadoc
1   /*
2    *  jDTAUS Banking Charset Providers
3    *  Copyright (C) 2005 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.banking.charsets.spi;
22  
23  import java.nio.ByteBuffer;
24  import java.nio.CharBuffer;
25  import java.nio.charset.Charset;
26  import java.nio.charset.CharsetDecoder;
27  import java.nio.charset.CharsetEncoder;
28  import java.nio.charset.CoderResult;
29  import java.nio.charset.CodingErrorAction;
30  import java.nio.charset.spi.CharsetProvider;
31  import java.util.Iterator;
32  import java.util.NoSuchElementException;
33  
34  /**
35   * {@code CharsetProvider} for DIN-66003 Charset.
36   * <p>
37   * Name: DIN_66003<br>
38   * MIBenum: 24<br>
39   * Source: ECMA registry<br>
40   * Alias: iso-ir-21<br>
41   * Alias: de<br>
42   * Alias: ISO646-DE<br>
43   * Alias: csISO21German<br>
44   * See: RFC1345, KXS2
45   *
46   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
47   * @version $JDTAUS: DIN66003CharsetProvider.java 8661 2012-09-27 11:29:58Z schulte $
48   */
49  public class DIN66003CharsetProvider extends CharsetProvider
50  {
51  
52      /** Common name. */
53      static final String COMMON_NAME = "DIN_66003";
54  
55      /** Alias names. */
56      static final String[] ALIAS_NAMES =
57      {
58          "iso-ir-21", "de", "iso646-de", "csiso21german"
59      };
60  
61      /** Supported character set names. */
62      static final String[] SUPPORTED_NAMES =
63      {
64          COMMON_NAME.toLowerCase(), "iso-ir-21", "de", "iso646-de", "csiso21german"
65      };
66  
67      static final char[] BYTE_TO_CHAR = new char[ 0xFF ];
68  
69      static final byte[] CHAR_TO_BYTE = new byte[ 0xFF ];
70  
71      static
72      {
73          for ( int i = 0x7F; i >= 0; i-- )
74          {
75              CHAR_TO_BYTE[i] = (byte) i;
76              BYTE_TO_CHAR[i] = (char) i;
77          }
78  
79          CHAR_TO_BYTE['\u00A7'] = (byte) 0x40;
80          CHAR_TO_BYTE['\u00C4'] = (byte) 0x5B;
81          CHAR_TO_BYTE['\u00D6'] = (byte) 0x5C;
82          CHAR_TO_BYTE['\u00DC'] = (byte) 0x5D;
83          CHAR_TO_BYTE['\u00E4'] = (byte) 0x7B;
84          CHAR_TO_BYTE['\u00F6'] = (byte) 0x7C;
85          CHAR_TO_BYTE['\u00FC'] = (byte) 0x7D;
86          CHAR_TO_BYTE['\u00DF'] = (byte) 0x7E;
87  
88          BYTE_TO_CHAR[0x40] = '\u00A7';
89          BYTE_TO_CHAR[0x5B] = '\u00C4';
90          BYTE_TO_CHAR[0x5C] = '\u00D6';
91          BYTE_TO_CHAR[0x5D] = '\u00DC';
92          BYTE_TO_CHAR[0x7B] = '\u00E4';
93          BYTE_TO_CHAR[0x7C] = '\u00F6';
94          BYTE_TO_CHAR[0x7D] = '\u00FC';
95          BYTE_TO_CHAR[0x7E] = '\u00DF';
96      }
97  
98      /** Creates a new {@code DIN66003CharsetProvider} instance. */
99      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. */
160 class 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 
193 class 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 
275 class 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 }