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 java.io.UnsupportedEncodingException;
14  import java.util.ArrayList;
15  import java.util.List;
16  
17  import com.legstar.coxb.CobolContext;
18  import com.legstar.coxb.ICobolArrayDbcsBinding;
19  import com.legstar.coxb.ICobolDbcsBinding;
20  import com.legstar.coxb.convert.ICobolDbcsConverter;
21  import com.legstar.coxb.convert.CobolConversionException;
22  import com.legstar.coxb.host.HostData;
23  import com.legstar.coxb.host.HostException;
24  
25  /**
26   * This is a concrete implementation of marshal/unmarshal operations of java
27   * strings to cobol DBCS (PIC G DISPLAY-1) character strings.
28   * 
29   * @author Fady Moussallam
30   * 
31   */
32  public class CobolDbcsSimpleConverter extends CobolSimpleConverter
33          implements ICobolDbcsConverter {
34  
35      /** Character used on mainframes to start a DBCS sequence. */
36      public static final char SHIFT_IN = 0x0e;
37  
38      /** Character used on mainframes to stop a DBCS sequence. */
39      public static final char SHIFT_OUT = 0x0f;
40  
41      /**
42       * @param cobolContext the Cobol compiler parameters in effect
43       */
44      public CobolDbcsSimpleConverter(
45              final CobolContext cobolContext) {
46          super(cobolContext);
47      }
48  
49      /** {@inheritDoc} */
50      public int toHost(
51              final ICobolDbcsBinding ce,
52              final byte[] hostTarget,
53              final int offset)
54              throws HostException {
55          int newOffset = 0;
56          try {
57              newOffset = toHostSingle(ce.getStringValue(),
58                      getCobolContext().getHostCharsetName(),
59                      ce.getByteLength(),
60                      ce.isJustifiedRight(),
61                      hostTarget,
62                      offset);
63          } catch (CobolConversionException e) {
64              throwHostException(ce, e);
65          }
66          return newOffset;
67      }
68  
69      /** {@inheritDoc} */
70      public int toHost(
71              final ICobolArrayDbcsBinding ce,
72              final byte[] hostTarget,
73              final int offset,
74              final int currentOccurs)
75              throws HostException {
76          int newOffset = offset;
77          try {
78              for (String javaSource : ce.getStringList()) {
79                  newOffset = toHostSingle(javaSource,
80                          getCobolContext().getHostCharsetName(),
81                          ce.getItemByteLength(),
82                          ce.isJustifiedRight(),
83                          hostTarget,
84                          newOffset);
85              }
86              /* If necessary, fill in the array with missing items */
87              for (int i = ce.getStringList().size(); i < currentOccurs; i++) {
88                  newOffset = toHostSingle("",
89                          getCobolContext().getHostCharsetName(),
90                          ce.getItemByteLength(),
91                          ce.isJustifiedRight(),
92                          hostTarget,
93                          newOffset);
94              }
95          } catch (CobolConversionException e) {
96              throwHostException(ce, e);
97          }
98          return newOffset;
99      }
100 
101     /** {@inheritDoc} */
102     public int fromHost(
103             final ICobolDbcsBinding ce,
104             final byte[] hostSource,
105             final int offset)
106             throws HostException {
107         int newOffset = offset;
108         try {
109             String javaString = fromHostSingle(
110                     getCobolContext().getHostCharsetName(),
111                     ce.getByteLength(),
112                     hostSource,
113                     newOffset);
114             ce.setStringValue(javaString);
115             newOffset += ce.getByteLength();
116         } catch (CobolConversionException e) {
117             throwHostException(ce, e);
118         }
119         return newOffset;
120     }
121 
122     /** {@inheritDoc} */
123     public int fromHost(
124             final ICobolArrayDbcsBinding ce,
125             final byte[] hostSource,
126             final int offset,
127             final int currentOccurs)
128             throws HostException {
129         List < String > lArray = new ArrayList < String >();
130         int newOffset = offset;
131         try {
132             for (int i = 0; i < currentOccurs; i++) {
133                 String javaString = fromHostSingle(
134                         getCobolContext().getHostCharsetName(),
135                         ce.getItemByteLength(),
136                         hostSource,
137                         newOffset);
138                 lArray.add(javaString);
139                 newOffset += ce.getItemByteLength();
140             }
141             ce.setStringList(lArray);
142         } catch (CobolConversionException e) {
143             throwHostException(ce, e);
144         }
145         return newOffset;
146     }
147 
148     /**
149      * Converts a Java String to a host character stream within the host
150      * character set.
151      * 
152      * @param javaString java string to convert
153      * @param hostCharsetName host character set
154      * @param cobolByteLength host byte length
155      * @param isJustifiedRight is Cobol data right justified
156      * @param hostTarget target host buffer
157      * @param offset offset in target host buffer
158      * @return offset after host buffer is updated
159      * @throws CobolConversionException if conversion fails
160      */
161     public static final int toHostSingle(
162             final String javaString,
163             final String hostCharsetName,
164             final int cobolByteLength,
165             final boolean isJustifiedRight,
166             final byte[] hostTarget,
167             final int offset)
168             throws CobolConversionException {
169 
170         /* Check that we are still within the host target range */
171         int lastOffset = offset + cobolByteLength;
172         if (lastOffset > hostTarget.length) {
173             throw (new CobolConversionException(
174                     "Attempt to write past end of host source buffer",
175                     new HostData(hostTarget), offset, cobolByteLength));
176         }
177 
178         /*
179          * HostData is obtained by converting the java String content to the
180          * target host character set.
181          */
182         byte[] hostSource;
183 
184         /*
185          * See how many host bytes would be needed to hold the converted
186          * string
187          */
188         try {
189             if (javaString == null) {
190                 hostSource =
191                         "".getBytes(hostCharsetName);
192             } else {
193                 hostSource =
194                         javaString.getBytes(hostCharsetName);
195             }
196 
197             /*
198              * The target host element might be larger than the converted java
199              * String and might have to be right or left justified. The padding
200              * character is a space character.
201              */
202             int iSource = 0;
203             int iTarget = offset;
204             int iLength = hostSource.length;
205 
206             if (iLength > 0) {
207 
208                 /*
209                  * The java conversion always adds shift-in/shift-out delimiters
210                  * which are not needed for pure DBCS fields.
211                  */
212                 if (hostSource[0] == SHIFT_IN) {
213                     iSource++;
214                 }
215                 if (hostSource[hostSource.length - 1] == SHIFT_OUT) {
216                     iLength--;
217                 }
218             }
219 
220             /* Pad with initial spaces if necessary */
221             if (isJustifiedRight) {
222                 iTarget += pad(hostTarget,
223                         iTarget, (lastOffset - iLength),
224                         hostCharsetName);
225             }
226 
227             /* Continue on with source content */
228             while (iSource < iLength && iTarget < lastOffset) {
229                 hostTarget[iTarget] = hostSource[iSource];
230                 iSource++;
231                 iTarget++;
232             }
233 
234             /* Pad with final spaces if necessary */
235             if (!isJustifiedRight) {
236                 pad(hostTarget,
237                         iTarget, lastOffset,
238                         hostCharsetName);
239             }
240             return lastOffset;
241         } catch (UnsupportedEncodingException uee) {
242             throw new CobolConversionException(
243                     "UnsupportedEncodingException:" + uee.getMessage());
244         }
245 
246     }
247 
248     /**
249      * Converts a host character string into a Java string.
250      * 
251      * @param hostCharsetName host character set
252      * @param cobolByteLength host byte length
253      * @param hostSource source host buffer
254      * @param offset offset in source host buffer
255      * @return offset after host buffer is read
256      * @throws CobolConversionException if conversion fails
257      */
258     public static final String fromHostSingle(
259             final String hostCharsetName,
260             final int cobolByteLength,
261             final byte[] hostSource,
262             final int offset)
263             throws CobolConversionException {
264 
265         String javaString = null;
266         int javaStringLength = cobolByteLength;
267 
268         /*
269          * Check that we are still within the host source range.
270          * If not, consider the host optimized its payload by truncating
271          * trailing nulls in which case, we just need to process the
272          * characters returned if any.
273          */
274         int lastOffset = offset + cobolByteLength;
275         if (lastOffset > hostSource.length) {
276             if (offset >= hostSource.length) {
277                 return javaString;
278             } else {
279                 javaStringLength = hostSource.length - offset;
280             }
281         }
282 
283         /*
284          * The host is not expected to wrap string with shift-in/shift-out
285          * while java expects that. We need to append those.
286          */
287         byte[] shiftHostSource = new byte[javaStringLength + 2];
288         shiftHostSource[0] = SHIFT_IN;
289         System.arraycopy(hostSource, offset, shiftHostSource, 1,
290                 javaStringLength);
291         shiftHostSource[shiftHostSource.length - 1] = SHIFT_OUT;
292         javaStringLength += 2;
293 
294         /*
295          * The Java String is obtained by translating from the host code page
296          * to the local code page.
297          */
298         try {
299             javaString = new String(
300                     shiftHostSource, 0, javaStringLength,
301                     hostCharsetName);
302             /*
303              * Some low-value characters may have slipped into the resulting
304              * string.
305              */
306             if (javaString.indexOf("\0") != -1) {
307                 javaString = javaString.replace('\0', ' ');
308             }
309         } catch (UnsupportedEncodingException uee) {
310             throw new CobolConversionException(
311                     "UnsupportedEncodingException:" + uee.getMessage());
312         }
313 
314         return javaString.trim();
315     }
316 
317     /**
318      * Determines the padding character depending on the target character set.
319      * 
320      * @param hostCharsetName host character set
321      * @return the padding character
322      * @throws UnsupportedEncodingException if padding character cannot be
323      *             translated
324      */
325     private static byte getPadChar(
326             final String hostCharsetName) throws UnsupportedEncodingException {
327         return " ".getBytes(hostCharsetName)[0];
328     }
329 
330     /**
331      * Pads a byte array with the padding character corresponding to the target
332      * host character set.
333      * <p/>
334      * Padding begins at the specified beginIndex and extends to the character
335      * at index endIndex - 1
336      * 
337      * @param bytes the byte array to pad
338      * @param beginIndex what index to start from
339      * @param endIndex index of first character that is not to be padded
340      * @param hostCharsetName host character set
341      * @return the number of padding characters used
342      * @throws UnsupportedEncodingException if padding character cannot be
343      *             translated
344      */
345     public static int pad(
346             final byte[] bytes,
347             final int beginIndex,
348             final int endIndex,
349             final String hostCharsetName) throws UnsupportedEncodingException {
350         byte padChar = getPadChar(hostCharsetName);
351         int j = 0;
352         for (int i = beginIndex; i < endIndex; i++) {
353             bytes[i] = padChar;
354             j++;
355         }
356         return j;
357     }
358 }