View Javadoc

1   /*******************************************************************************
2    * Copyright (c) 2015 LegSem.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the GNU Lesser Public License v2.1
5    * which accompanies this distribution, and is available at
6    * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
7    * 
8    * Contributors:
9    *     LegSem - initial API and implementation
10   ******************************************************************************/
11  package com.legstar.coxb.convert.simple;
12  
13  import com.legstar.coxb.CobolContext;
14  import com.legstar.coxb.ICobolArrayZonedDecimalBinding;
15  import com.legstar.coxb.ICobolZonedDecimalBinding;
16  import com.legstar.coxb.convert.ICobolZonedDecimalConverter;
17  import com.legstar.coxb.convert.CobolConversionException;
18  import com.legstar.coxb.host.HostData;
19  import com.legstar.coxb.host.HostException;
20  
21  import java.math.BigDecimal;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  /**
26   * This is a concrete implementation of marshal/unmarshal operations of java 
27   * numerics to cobol zoned decimals.
28   *
29   * @author Fady Moussallam
30   * @deprecated
31   */
32  public class CobolZonedDecimalSimpleConverterNoCodePage
33  extends CobolSimpleConverter
34  implements ICobolZonedDecimalConverter {
35  
36      /** Ebcdic code point for plus sign. */
37      private static final byte PLUS_EBCDIC = 0x4E; 
38  
39      /** Ebcdic code point for minus sign. */
40      private static final byte MINUS_EBCDIC = 0x60; 
41  
42      /**
43       * @param cobolContext the Cobol compiler parameters in effect
44       */
45      public CobolZonedDecimalSimpleConverterNoCodePage(
46              final CobolContext cobolContext) {
47          super(cobolContext);
48      }
49  
50      /** {@inheritDoc} */
51      public int toHost(
52              final ICobolZonedDecimalBinding ce,
53              final byte[] hostTarget,
54              final int offset) throws HostException {
55          int newOffset = 0;
56          try {
57              newOffset = toHostSingle(ce.getBigDecimalValue(),
58                      ce.getByteLength(),
59                      ce.getTotalDigits(),
60                      ce.getFractionDigits(),
61                      ce.isSigned(),
62                      ce.isSignSeparate(),
63                      ce.isSignLeading(),
64                      hostTarget,
65                      offset);
66          } catch (CobolConversionException e) {
67              throwHostException(ce, e);
68          }
69          return newOffset;
70      }
71  
72      /** {@inheritDoc} */
73      public int toHost(
74              final ICobolArrayZonedDecimalBinding ce,
75              final byte[] hostTarget,
76              final int offset,
77              final int currentOccurs)
78      throws HostException {
79          int newOffset = offset;
80          try {
81              for (BigDecimal javaSource : ce.getBigDecimalList()) {
82                  newOffset = toHostSingle(javaSource,
83                          ce.getItemByteLength(),
84                          ce.getTotalDigits(),
85                          ce.getFractionDigits(),
86                          ce.isSigned(),
87                          ce.isSignSeparate(),
88                          ce.isSignLeading(),
89                          hostTarget,
90                          newOffset);
91              }
92              /* If necessary, fill in the array with missing items */
93              for (int i = ce.getBigDecimalList().size();
94              i < currentOccurs; i++) {
95                  newOffset = toHostSingle(BigDecimal.ZERO,
96                          ce.getItemByteLength(),
97                          ce.getTotalDigits(),
98                          ce.getFractionDigits(),
99                          ce.isSignSeparate(),
100                         ce.isSignLeading(),
101                         ce.isSigned(),
102                         hostTarget,
103                         newOffset);
104             }
105         } catch (CobolConversionException e) {
106             throwHostException(ce, e);
107         }
108         return newOffset;
109     }
110 
111     /** {@inheritDoc} */
112     public int fromHost(
113             final ICobolZonedDecimalBinding ce,
114             final byte[] hostSource,
115             final int offset)
116     throws HostException {
117         int newOffset = offset;
118         try {
119             BigDecimal javaDecimal = fromHostSingle(ce.getByteLength(),
120                     ce.getTotalDigits(),
121                     ce.getFractionDigits(),
122                     ce.isSigned(),
123                     ce.isSignSeparate(),
124                     ce.isSignLeading(),
125                     hostSource,
126                     newOffset);
127             ce.setBigDecimalValue(javaDecimal);
128             newOffset += ce.getByteLength();
129         } catch (CobolConversionException e) {
130             throwHostException(ce, e);
131         }
132         return newOffset;
133     }
134 
135     /** {@inheritDoc} */
136     public int fromHost(
137             final ICobolArrayZonedDecimalBinding ce,
138             final byte[] hostSource,
139             final int offset,
140             final int currentOccurs)
141     throws HostException {
142         List < BigDecimal > lArray = new ArrayList < BigDecimal >();
143         int newOffset = offset;
144         try {
145             for (int i = 0; i < currentOccurs; i++) {
146                 BigDecimal javaDecimal = fromHostSingle(ce.getItemByteLength(),
147                         ce.getTotalDigits(),
148                         ce.getFractionDigits(),
149                         ce.isSigned(),
150                         ce.isSignSeparate(),
151                         ce.isSignLeading(),
152                         hostSource,
153                         newOffset);
154                 lArray.add(javaDecimal);
155                 newOffset += ce.getItemByteLength();
156             }
157             ce.setBigDecimalList(lArray);
158         } catch (CobolConversionException e) {
159             throwHostException(ce, e);
160         }
161         return newOffset;
162     }
163 
164     /**
165      *  Converts a Java BigDecimal to a host zoned decimal.
166      * 
167      * @param javaDecimal java decimal to convert
168      * @param cobolByteLength host byte length
169      * @param totalDigits Cobol element total number of digits
170      * @param fractionDigits Cobol element number of fractional digits
171      * @param isSigned Cobol element is signed or unsigned
172      * @param isSignSeparate Cobol element sign occupies own byte
173      * @param isSignLeading Cobol element sign in first byte
174      * @param hostTarget target host buffer
175      * @param offset offset in target host buffer
176      * @return offset after host buffer is updated
177      * @throws CobolConversionException if conversion fails
178      */
179     public static final int toHostSingle(
180             final BigDecimal javaDecimal,
181             final int cobolByteLength,
182             final int totalDigits,
183             final int fractionDigits,
184             final boolean isSigned,
185             final boolean isSignSeparate,
186             final boolean isSignLeading,
187             final byte[] hostTarget,
188             final int offset)
189     throws CobolConversionException {
190 
191         /* Check that we are still within the host target range */
192         int lastOffset = offset + cobolByteLength;
193         if (lastOffset > hostTarget.length) {
194             throw (new CobolConversionException(
195                     "Attempt to write past end of host source buffer",
196                     new HostData(hostTarget), offset, cobolByteLength));
197         }
198 
199         // Get a string representation of the decimal value
200         String sDecimal;
201         if (javaDecimal == null) {
202             sDecimal = "0";
203         } else {
204             sDecimal = javaDecimal.toString();
205             /* if the Java decimal has a different scale than target cobol 
206              * field, adjust scale */
207             if (javaDecimal.scale() != fractionDigits) {
208                 sDecimal = javaDecimal.setScale(
209                         fractionDigits, BigDecimal.ROUND_DOWN).toString();
210             }
211         }
212 
213         /* Determine the number of digits that the java decimal holds */
214         int javaDigits = 0;
215         for (int i = 0; i < sDecimal.length(); i++) {
216             if (Character.isDigit(sDecimal.charAt(i))) {
217                 javaDigits++;
218             }
219         }
220 
221         if (javaDigits > totalDigits) {
222             throw (new CobolConversionException(
223                     "BigDecimal value too large for target Cobol field",
224                     new HostData(hostTarget), offset, cobolByteLength));
225         }
226 
227 
228         /* Number of digits that are needed to pad the java value if it has
229          * less digits than the target cobol field */
230         int pad = totalDigits - javaDigits;
231 
232         int iTarget = offset;   /* points to current byte in host data */
233 
234         /* Reserve first byte of target for sign if needed */
235         if (isSigned && isSignSeparate && isSignLeading) {
236             iTarget++;
237         }
238 
239         /* Pad to the left with zeroes as necessary */
240         for (int i = 0; i < pad; i++) {
241             hostTarget[iTarget] = (byte) 0xF0;
242             iTarget++;
243         }
244 
245         /* Preposition all digits without consideration for sign */
246         for (int i = 0; i < sDecimal.length(); i++) {
247             char sC = sDecimal.charAt(i);
248             if (Character.isDigit(sC)) {
249                 hostTarget[iTarget] =
250                     (byte) (16 * 0x0F + Character.digit(sC, 10));
251                 iTarget++;
252             }
253         }
254 
255         /* Position the sign either separately or imbedded */
256         if (isSigned) {
257             if (isSignSeparate) {
258                 if (isSignLeading) {
259                     if (sDecimal.charAt(0) == '-') {
260                         hostTarget[offset] = MINUS_EBCDIC;
261                     } else {
262                         hostTarget[offset] = PLUS_EBCDIC;
263                     }
264                 } else {
265                     if (sDecimal.charAt(0) == '-') {
266                         hostTarget[iTarget] = MINUS_EBCDIC;
267                     } else {
268                         hostTarget[iTarget] = PLUS_EBCDIC;
269                     }
270                     iTarget++;
271                 }
272             } else {
273                 if (isSignLeading) {
274                     if (sDecimal.charAt(0) == '-') {
275                         hostTarget[offset] = (byte) (hostTarget[offset] - 0x20);
276                     } else {
277                         hostTarget[offset] = (byte) (hostTarget[offset] - 0x30);
278                     }
279                 } else {
280                     if (sDecimal.charAt(0) == '-') {
281                         hostTarget[iTarget - 1] =
282                             (byte) (hostTarget[iTarget - 1] - 0x20);
283                     } else {
284                         hostTarget[iTarget - 1] =
285                             (byte) (hostTarget[iTarget - 1] - 0x30);
286                     }
287                 }
288             }
289         }
290 
291         return iTarget;
292     }
293 
294     /** Converts a host packed decimal to a Java BigDecimal.
295      * 
296      * @param cobolByteLength host byte length
297      * @param totalDigits Cobol element total number of digits
298      * @param fractionDigits Cobol element number of fractional digits
299      * @param isSigned Cobol element is signed or unsigned
300      * @param isSignSeparate Cobol element sign occupies own byte
301      * @param isSignLeading Cobol element sign in first byte
302      * @param hostSource source host buffer
303      * @param offset offset in source host buffer
304      * @return offset after host buffer is read
305      * @throws CobolConversionException if conversion fails
306      */
307     public static final BigDecimal fromHostSingle(
308             final int cobolByteLength,
309             final int totalDigits,
310             final int fractionDigits,
311             final boolean isSigned,
312             final boolean isSignSeparate,
313             final boolean isSignLeading,
314             final byte[] hostSource,
315             final int offset)
316     throws CobolConversionException {
317 
318         /* To initialize the BigDecimal, we construct a String that represents
319          * the decimal value held in the Cobol zoned decimal */
320         StringBuffer sDecimal = new StringBuffer();
321 
322         int lastOffset = offset + cobolByteLength;
323 
324         /* Check that we are still within the host source range.
325          * If not, consider the host optimized its payload by truncating
326          * trailing nulls in which case, we just need to initialize and return. */
327         if (lastOffset > hostSource.length) {
328             return new BigDecimal(0);
329         }
330         if (lastOffset < 1) {
331             throw (new CobolConversionException(
332                     "Invalid host byte length",
333                     new HostData(hostSource), offset, cobolByteLength));
334         }
335 
336         /* First determine the sign of this decimal */
337         if (isSigned) {
338             /* If sign is separate check leading or trailing byte for a minus
339              * sign */
340             if (isSignSeparate) {
341                 if (isSignLeading)  {
342                     if (hostSource[offset] == MINUS_EBCDIC) {
343                         sDecimal.append('-');
344                     }
345                 } else {
346                     if (hostSource[lastOffset - 1] == MINUS_EBCDIC) {
347                         sDecimal.append('-');
348                     }
349                 }
350             } else {
351                 /* If sign is imbedded check leading or trailing byte for a
352                  *  minus sign */
353                 int s = (isSignLeading) ? (hostSource[offset] & 0xF0)
354                         : (hostSource[lastOffset - 1]  & 0xF0);
355                 if (s == 0xd0) {
356                     sDecimal.append('-');
357                 } else {
358                     if (s != 0xc0 && s != 0xf0) {
359                         throw (new CobolConversionException(
360                                 "Host data sign byte is not a valid zoned decimal byte",
361                                 new HostData(hostSource), offset, cobolByteLength));
362                     }
363                 }
364             }
365         }
366 
367         /* Each byte holds 1 digit */
368         int integerPart = 0;
369         for (int i = offset; i < lastOffset; i++) {
370 
371             /* Ignore the potential leading sign */
372             if ((i == offset)
373                     &&
374                     isSigned && isSignSeparate && isSignLeading) {
375                 continue;
376             }
377 
378             /* Ignore the potential trailing sign */
379             if ((i == (lastOffset - 1))
380                     &&
381                     isSigned && isSignSeparate && !isSignLeading) {
382                 continue;
383             }
384 
385             String sByte = Integer.toHexString(
386                     hostSource[i] & 0xFF | 0x100).substring(1, 3);
387 
388             /* If the integer part is exhausted, place a decimal point */
389             if (integerPart == (totalDigits - fractionDigits)) {
390                 sDecimal.append('.');
391             }
392 
393             if (!Character.isDigit(sByte.charAt(1))) {
394                 throw (new CobolConversionException(
395                         "Host data contains a byte that is not a valid zoned"
396                         + " decimal byte",
397                         new HostData(hostSource), offset, cobolByteLength));
398             }
399 
400             sDecimal.append(sByte.charAt(1));
401             integerPart += 1;
402         }
403 
404         return new BigDecimal(sDecimal.toString());
405     }
406 }