Source code: com/yaftp/utils/PackedDecimal.java
1 /**
2 *
3 * CopyRights Jean-Yves MENGANT 1999,2000,2001,2002
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 package com.yaftp.utils ;
21
22 /**
23 Copyright Jean-Yves MENGANT 1998,1999,2000
24
25
26 The class bellow deals with PACKED DECIMAL
27 conversions A portable conversions
28
29 Only the final PackedDecimal class should be used
30 other class are for internal usage only
31
32 NumberFormatException may be Thrown by this class on
33 invalid packed decimal format
34
35 // Construct using an existing byte array(Supose to point on
36 // a valid packed decimal format or to be translated later
37 // with toPackedDecimal method
38 public PackedDecimal( byte Packed_Origin[] ,
39 int Offset ,
40 int IntSize ,
41 int DecSize
42 )
43
44 // Build a packed decimal from a string number
45 public void toPackedDecimal( String Number )
46
47 // Translate current Packed into a String representation
48 public String toString()
49
50 @author Jean-Yves MENGANT
51
52 */
53
54 /* Object used for increment / decrement passed to methods */
55
56 class Decimal_Increment {
57
58 private int _Inc ;
59
60 // Public methods
61 public void SetValue( int Value ) { _Inc = Value ; }
62 public int Increment()
63 {
64 _Inc++ ;
65 return(_Inc-1) ; // Return value before increment
66 }
67
68 public int Decrement()
69 {
70 _Inc-- ;
71 return(_Inc+1) ; // Return value after increment
72 }
73 public int GetValue() { return _Inc ; }
74 }
75
76 class DecimalString {
77
78 private final static byte MAX_DECIMAL_SIZE = 20 ; // Max size of a decimal num
79
80 private char _IntPart[] ;
81 private int _IntSize ;
82 private boolean _Negative_Number ;
83 private char _DecPart[] ;
84 private int _DecSize ;
85
86 /* Public method on WORK decimal class starts here */
87
88
89 public DecimalString( String DecString )
90 throws NumberFormatException
91 {
92 boolean Decimal_Part = false ;
93 byte Ii = 0 ;
94 int Len = DecString.length() ;
95 int Decimal_Pos = 0 ;
96
97 _DecPart = new char[MAX_DECIMAL_SIZE] ;
98 _IntPart = new char[MAX_DECIMAL_SIZE] ;
99 _Negative_Number = false ;
100
101 while ( Ii < Len )
102 {
103 char CurChar = DecString.charAt(Ii) ;
104
105 switch( CurChar )
106 {
107 case '+' :
108 if ( Ii != 0 )
109 throw new NumberFormatException("Invalid + sign position");
110 break ;
111
112 case '-' :
113 if ( Ii != 0 )
114 throw new NumberFormatException("Invalid - sign position");
115 else
116 _Negative_Number = true ;
117 break ;
118
119 case '.' :
120 Decimal_Part = true ;
121 Decimal_Pos = 0 ;
122 break ;
123
124 case '0' :
125 case '1' :
126 case '2' :
127 case '3' :
128 case '4' :
129 case '5' :
130 case '6' :
131 case '7' :
132 case '8' :
133 case '9' :
134 if ( Decimal_Part )
135 {
136 _DecPart[Decimal_Pos++] = CurChar ;
137 _DecSize++ ;
138 }
139 else
140 {
141 _IntPart[_IntSize] = CurChar ;
142 _IntSize++ ;
143 }
144 break ;
145
146 default :
147 throw new NumberFormatException(
148 "PackedDecimal.DecimalString : unexpected character received");
149 }
150 Ii++ ;
151 }
152
153 }
154
155 // Accessors
156 public boolean IsNegative()
157 { return _Negative_Number ; }
158
159 public int GiveIntSize() { return _IntSize ; }
160
161 /**
162 Returns Digit corresponding to Pos and decrement Pos
163 */
164 public byte DecDigit( Decimal_Increment Pos )
165 {
166 if ( Pos.GetValue() > _DecSize )
167 {
168 Pos.Decrement() ;
169 return 0 ;
170 }
171 else
172 {
173 Pos.Decrement() ;
174 return BYTEFX.MAKEBYTE (
175 BYTEFX.LOWNIBBLE( (byte)(_DecPart[Pos.GetValue()]) ),
176 (byte)(0)
177 ) ;
178 }
179 }
180
181 /**
182 @return Digit corresponding to Pos in integer part
183 */
184 public byte IntDigit ( Decimal_Increment Pos )
185 {
186 Pos.Decrement() ;
187 return BYTEFX.MAKEBYTE(
188 BYTEFX.LOWNIBBLE( (byte)(_IntPart[Pos.GetValue()]) ) ,
189 BYTEFX.LOWNIBBLE( (byte)(0) )
190 ) ;
191 }
192 } ;
193
194 public class PackedDecimal extends DataStructure {
195
196 private static final byte DEFAULT_NEGATIVE = 0x0d ;
197 private static final byte DEFAULT_POSITIVE = 0x0c ;
198
199 private static final byte ASCII_HIGH_CHAR_NIBBLE = (byte)(0x30) ;
200 private static final int MAX_DECIMAL_STRING_SIZE = 100 ;
201 private static final byte CBL_SIGN_POS1 = (byte)(0x0A) ;
202 private static final byte CBL_SIGN_POS2 = (byte)(0x0C) ;
203 private static final byte CBL_SIGN_POS3 = (byte)(0x0E) ;
204 private static final byte CBL_SIGN_POS4 = (byte)(0x0F) ;
205 /* Consider unsigned as positive !!!! */
206
207 private byte _PackedWk[] ; // Use this as private internal work
208 private int _IntSize ; // Total number of non decimal digits
209 private int _DecSize ; // total number of decimal digits
210 private int _Logical_Dest_Size ; // Total logical size
211
212 // Public access starts here
213
214 /**
215 giving int Size + dec size returns physical size
216 necessary to store the packed decimal data
217 */
218 public static int getPackedSize( int intSize , int decSize )
219 {
220 int Logical_Dest_Size = intSize + decSize ;
221
222 if ( Logical_Dest_Size % 2 == 0 )
223 return ( Logical_Dest_Size / 2 ) + 1 ;
224 else
225 return ( Logical_Dest_Size + 1 ) / 2 ;
226 }
227
228 /**
229 Construct using an existing byte array(Supose to point on
230 a valid packed decimal format or to be translated later
231 with toPackedDecimal method
232
233 @param Packed_Origin byte array containing COBOL packed decimal data
234 @param Offset offset of packed decimal data in array
235 @param IntSize non decimal part size
236 @param DecSize decimal part size
237
238 */
239 public PackedDecimal( byte Packed_Origin[] ,
240 int Offset ,
241 int IntSize ,
242 int DecSize
243 )
244 {
245 super( Packed_Origin , Offset , 0 ) ;
246
247 _IntSize = IntSize ;
248 _DecSize = DecSize ;
249 _Logical_Dest_Size = IntSize + DecSize ;
250 _Size = getPackedSize( IntSize , DecSize ) ;
251 }
252
253 /**
254 Build a packed decimal from a string number
255
256 Convert the in String +-99999.99 in an packed decimal IBM data
257 Flow -> Each digit is a 0..9 Numerical value last digit is the sign
258 digit : A|C|E|F => + ; B|D => - ; the decimal point is virtual
259 its position is defined in the second byte of dec_len
260
261 @param Number decimal String representation to be converted
262 */
263 public void toPackedDecimal( String Number )
264 throws NumberFormatException
265 {
266 Decimal_Increment Ii = new Decimal_Increment() ;
267 Decimal_Increment Jj = new Decimal_Increment() ;
268
269 boolean Decimal_Part = false ;
270 int Physical_Dest_Size ;
271 byte Digit ;
272 int OutPos ;
273 boolean High = true ;
274 int CurIntSize = 0 ;
275
276 // Build Local Wk Decimal String
277 DecimalString Wk = new DecimalString( Number ) ;
278 _PackedWk = new byte[_Size] ; // Allocate working
279
280 OutPos = _Size -1 ;
281
282 if ( Wk.IsNegative() )
283 _PackedWk[OutPos] = DEFAULT_NEGATIVE ;
284 else
285 _PackedWk[OutPos] = DEFAULT_POSITIVE ;
286
287 Ii.SetValue(_DecSize) ;
288 Jj.SetValue(Wk.GiveIntSize()) ;
289
290 if ( Ii.GetValue() != 0 )
291 Decimal_Part = true ;
292
293 while ( ( OutPos != -1 ) &&
294 ( Jj.GetValue() != 0 )
295 )
296 {
297 if ( Decimal_Part )
298 {
299 Digit = Wk.DecDigit(Ii) ;
300
301 if ( Ii.GetValue() == 0 )
302 Decimal_Part = false ;
303 }
304 else
305 Digit = Wk.IntDigit(Jj) ;
306
307 if ( High )
308 {
309 _PackedWk[OutPos] =
310 BYTEFX.MAKEBYTE ( BYTEFX.LOWNIBBLE( _PackedWk[OutPos] ) ,
311 BYTEFX.LOWNIBBLE( Digit )
312 ) ;
313 OutPos-- ;
314 }
315 else
316 _PackedWk[OutPos] =
317 BYTEFX.MAKEBYTE ( BYTEFX.LOWNIBBLE( Digit ) ,
318 BYTEFX.LOWNIBBLE( _PackedWk[OutPos] )
319 ) ;
320
321 High = !High ; // Switch to Next Digit
322 }
323
324 // Move Working to super Data Structure
325 System.arraycopy( _PackedWk , 0 , _Struct , _Offset , _Size ) ;
326 }
327
328
329 /**
330 Translate current Packed into a String representation
331
332 @return String representation of packed decimal
333 */
334 public String toString()
335 {
336 int Ii = 0 ;
337 int Sign_Location ;
338 int Logical_Length ;
339 int IOut = 0 ; /* position OutStrDecimal */
340 int IIn = 0 ; /* Position in InDecimal */
341 boolean _End = false ;/* =TRUE when number has been proceed */
342 /* =FALSE else */
343 boolean High = false ;/* =TRUE when on high nibble */
344 /* =FALSE when on low nibble */
345 /* of pIndecimal PACKED STRING */
346 boolean Significant = false ; /* Bypass leading non significant 0 */
347 byte Digit; /* extracted pIndecimal[IIn] */
348 int NbDigit ; /* number of proceed digit */
349 boolean EvenNbDigits ; /* True if number of digits in Dec number is even */
350 boolean InDecimalPart = false ;
351 String Sign = new String("") ;
352
353 /* Logical Data Length */
354 Logical_Length = _IntSize + _DecSize ; /* Len = Number of digits + Sign */
355 EvenNbDigits = ( ( Logical_Length % 2 ) == 0 ) ;
356 Logical_Length++ ;
357 Logical_Length += 2 ; /* Reserve space for '0.' if needed */
358
359 if ( _DecSize != 0 )
360 InDecimalPart = true ;
361
362 Sign_Location = IOut; /* Save Sign Position */
363 IOut++;
364
365 char OutStrDecimal[] = new char[MAX_DECIMAL_STRING_SIZE] ;
366
367 if ( _IntSize == 0 )
368 {
369 OutStrDecimal[IOut++]='0';
370 OutStrDecimal[IOut++]= '.';
371 }
372
373 NbDigit=0;
374
375 if ( EvenNbDigits )
376 High = true ; /* BYPASS High padding nibble */
377
378 while ( ! _End )
379 {
380 High = ! High ;
381
382 if ( High )
383 {
384 Digit= BYTEFX.HINIBBLE( _Struct[IIn]);
385 }
386 else
387 {
388 Digit= BYTEFX.LOWNIBBLE(_Struct[IIn]);
389 IIn++;
390 }
391
392 NbDigit++;
393 switch (Digit)
394 {
395 case CBL_SIGN_POS1 :
396 case CBL_SIGN_POS2 :
397 case CBL_SIGN_POS3 :
398 case CBL_SIGN_POS4 :
399 // Starting a positive number by '+' leads to
400 // number format exceptions when building BigDecimal from
401 // decimal string number so no leading sign is implicit '+'
402 _End = true ;
403 break;
404
405 case 0 :
406 case 1 :
407 case 2 :
408 case 3 :
409 case 4 :
410 case 5 :
411 case 6 :
412 case 7 :
413 case 8 :
414 case 9 :
415 if ( ( Digit != 0 ) || ( Significant ) )
416 {
417 Significant = true ; /* Anyway all incoming 0 will be significant */
418 OutStrDecimal[IOut++] = (char)(
419 BYTEFX.MAKEBYTE( Digit ,
420 BYTEFX.HINIBBLE(ASCII_HIGH_CHAR_NIBBLE)
421 )
422 ) ;
423 if ( ( NbDigit == _IntSize ) &&
424 ( _DecSize != 0 )
425 )
426 {
427 OutStrDecimal[IOut++] = '.';
428 InDecimalPart = false ;
429 }
430 }
431 break ;
432
433 default :
434 Sign = new String("-") ; /* Set Negative */
435 _End = true ;
436 break ;
437
438
439 } /* switch */
440 } /* while */
441
442 // A ZERO VALUE packed Decimal
443 if ( ! Significant )
444 return( new String("0") ) ;
445
446 if ( InDecimalPart ) // 0.9999 case with non null integer part defined
447 // in cobol pic 9(9)V99
448 return( Sign + (new String("0.")) + (new String(OutStrDecimal)).trim() ) ;
449 else
450 {
451 if ( Sign.length() != 0 )
452 { // Should be a negative number with leading '-'
453 OutStrDecimal[Sign_Location] = Sign.charAt(0) ;
454 return( new String(OutStrDecimal) ) ;
455 }
456 else // Positive number with leading blank
457 return (new String(OutStrDecimal)).trim() ;
458 }
459 }
460
461
462 /**
463 Just use the main method for Class unit testing
464 */
465 public static void main ( String Argv[] )
466 {
467
468 try
469 {
470 PackedDecimal MyPacked = new PackedDecimal( new byte[10] , 0 , 10 ,2 ) ;
471 MyPacked.toPackedDecimal("0.3542") ;
472 System.out.println( " Decimal value is : " + MyPacked.toString() ) ;
473
474 PackedDecimal MyPacked1 = new PackedDecimal( new byte[10] , 0 , 3 , 3 ) ;
475 MyPacked1.toPackedDecimal("1543.2545") ;
476 System.out.println( " Decimal value is : " + MyPacked1.toString() ) ;
477
478 PackedDecimal MyPacked2 = new PackedDecimal( new byte[10] , 0 , 10 ,2 ) ;
479 MyPacked2.toPackedDecimal("3.2") ;
480 System.out.println( " Decimal value is : " + MyPacked2.toString() ) ;
481
482 long StartTime = System.currentTimeMillis() ;
483
484 for ( int Ii = 0 ; Ii < 1000 ; Ii++ ) ;
485 {
486 MyPacked = new PackedDecimal( new byte[10] , 0 , 10 ,2 ) ;
487 MyPacked.toPackedDecimal("412") ;
488 }
489 long Elapsed = System.currentTimeMillis() - StartTime ;
490
491 System.out.println( " Decimal value is : " + MyPacked.toString() ) ;
492
493 System.out.println( "Loop Elapsed in : " + Elapsed ) ;
494
495 } catch ( NumberFormatException e )
496 { System.out.println(" Error : " + e ) ; }
497
498 }
499
500 }