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  import java.util.regex.Matcher;
17  import java.util.regex.Pattern;
18  
19  import com.legstar.coxb.CobolContext;
20  import com.legstar.coxb.ICobolArrayStringBinding;
21  import com.legstar.coxb.ICobolStringBinding;
22  import com.legstar.coxb.convert.CobolConversionException;
23  import com.legstar.coxb.convert.ICobolStringConverter;
24  import com.legstar.coxb.host.HostData;
25  import com.legstar.coxb.host.HostException;
26  
27  /**
28   * This is a concrete implementation of marshal/unmarshal operations of java
29   * strings to cobol character strings.
30   * 
31   * @author Fady Moussallam
32   * 
33   */
34  public class CobolStringSimpleConverter extends CobolSimpleConverter implements
35          ICobolStringConverter {
36  
37      /** Detects a string content as binary data. */
38      private static final Pattern BINARY_CONTENT_PATTERN = Pattern
39              .compile("0x[\\da-fA-F]+");
40  
41      /**
42       * @param cobolContext the Cobol compiler parameters in effect
43       */
44      public CobolStringSimpleConverter(final CobolContext cobolContext) {
45          super(cobolContext);
46      }
47  
48      /** {@inheritDoc} */
49      public int toHost(final ICobolStringBinding ce, final byte[] hostTarget,
50              final int offset) throws HostException {
51          int newOffset = 0;
52          try {
53              newOffset = toHostSingle(ce.getStringValue(), getCobolContext()
54                      .getHostCharsetName(), getCobolContext()
55                      .getAlphanumPaddingChar(), getCobolContext()
56                      .failOnAlphanumOverflow(), ce.getByteLength(),
57                      ce.isJustifiedRight(), hostTarget, offset);
58          } catch (CobolConversionException e) {
59              throwHostException(ce, e);
60          }
61          return newOffset;
62      }
63  
64      /** {@inheritDoc} */
65      public int toHost(final ICobolArrayStringBinding ce,
66              final byte[] hostTarget, final int offset, final int currentOccurs)
67              throws HostException {
68          int newOffset = offset;
69          try {
70              for (String javaSource : ce.getStringList()) {
71                  newOffset = toHostSingle(javaSource, getCobolContext()
72                          .getHostCharsetName(), getCobolContext()
73                          .getAlphanumPaddingChar(), getCobolContext()
74                          .failOnAlphanumOverflow(), ce.getItemByteLength(),
75                          ce.isJustifiedRight(), hostTarget, newOffset);
76              }
77              /* If necessary, fill in the array with missing items */
78              for (int i = ce.getStringList().size(); i < currentOccurs; i++) {
79                  newOffset = toHostSingle("", getCobolContext()
80                          .getHostCharsetName(), getCobolContext()
81                          .getAlphanumPaddingChar(), getCobolContext()
82                          .failOnAlphanumOverflow(), ce.getItemByteLength(),
83                          ce.isJustifiedRight(), hostTarget, newOffset);
84              }
85          } catch (CobolConversionException e) {
86              throwHostException(ce, e);
87          }
88          return newOffset;
89      }
90  
91      /** {@inheritDoc} */
92      public int fromHost(final ICobolStringBinding ce, final byte[] hostSource,
93              final int offset) throws HostException {
94          int newOffset = offset;
95          try {
96              String javaString = fromHostSingle(getCobolContext()
97                      .getHostCharsetName(), ce.getByteLength(), hostSource,
98                      newOffset);
99              ce.setStringValue(javaString);
100             newOffset += ce.getByteLength();
101         } catch (CobolConversionException e) {
102             throwHostException(ce, e);
103         }
104         return newOffset;
105     }
106 
107     /** {@inheritDoc} */
108     public int fromHost(final ICobolArrayStringBinding ce,
109             final byte[] hostSource, final int offset, final int currentOccurs)
110             throws HostException {
111         List < String > lArray = new ArrayList < String >();
112         int newOffset = offset;
113         try {
114             for (int i = 0; i < currentOccurs; i++) {
115                 String javaString = fromHostSingle(getCobolContext()
116                         .getHostCharsetName(), ce.getItemByteLength(),
117                         hostSource, newOffset);
118                 lArray.add(javaString);
119                 newOffset += ce.getItemByteLength();
120             }
121             ce.setStringList(lArray);
122         } catch (CobolConversionException e) {
123             throwHostException(ce, e);
124         }
125         return newOffset;
126     }
127 
128     /**
129      * Converts a Java String to a host character stream using the host
130      * character set unless content represents binary data.
131      * <p>
132      * Any character string following this pattern: '0x[\\da-fA-F]+' is
133      * interpreted as binary content.
134      * 
135      * @param javaString java string to convert
136      * @param hostCharsetName host character set
137      * @param paddingChar padding character
138      * @param cobolByteLength host byte length
139      * @param isJustifiedRight is Cobol data right justified
140      * @param hostTarget target host buffer
141      * @param offset offset in target host buffer
142      * @return offset after host buffer is updated
143      * @throws CobolConversionException if conversion fails
144      */
145     public static final int toHostSingle(final String javaString,
146             final String hostCharsetName, final Byte paddingChar,
147             final int cobolByteLength, final boolean isJustifiedRight,
148             final byte[] hostTarget, final int offset)
149             throws CobolConversionException {
150         return toHostSingle(javaString, hostCharsetName, paddingChar, false,
151                 cobolByteLength, isJustifiedRight, hostTarget, offset);
152     }
153 
154     /**
155      * Converts a Java String to a host character stream using the host
156      * character set unless content represents binary data.
157      * <p>
158      * Any character string following this pattern: '0x[\\da-fA-F]+' is
159      * interpreted as binary content.
160      * 
161      * @param javaString java string to convert
162      * @param hostCharsetName host character set
163      * @param paddingChar padding character
164      * @param failOnOverflow true if an exception should be raised if string
165      *            content does not fit in target alphanumeric data item
166      * @param cobolByteLength host byte length
167      * @param isJustifiedRight is Cobol data right justified
168      * @param hostTarget target host buffer
169      * @param offset offset in target host buffer
170      * @return offset after host buffer is updated
171      * @throws CobolConversionException if conversion fails
172      */
173     public static final int toHostSingle(final String javaString,
174             final String hostCharsetName, final Byte paddingChar,
175             final boolean failOnOverflow, final int cobolByteLength,
176             final boolean isJustifiedRight, final byte[] hostTarget,
177             final int offset) throws CobolConversionException {
178 
179         /* Check that we are still within the host target range */
180         int lastOffset = offset + cobolByteLength;
181         if (lastOffset > hostTarget.length) {
182             throw (new CobolConversionException(
183                     "Attempt to write past end of host source buffer",
184                     new HostData(hostTarget), offset, cobolByteLength));
185         }
186 
187         /* HostData before it is positioned in the hostTarget buffer. */
188         byte[] hostSource = new byte[0];
189         byte padChar;
190 
191         /*
192          * If data being passed represent binary content convert from hex string
193          * otherwise use the host charset for conversion.
194          */
195         if (isBinaryContent(javaString)) {
196             padChar = (paddingChar == null) ? 0x0 : paddingChar;
197             hostSource = HostData.toByteArray(javaString.substring(2));
198         } else {
199             try {
200                 padChar = (paddingChar == null) ? " ".getBytes(hostCharsetName)[0]
201                         : paddingChar;
202                 if (javaString != null) {
203                     hostSource = javaString.getBytes(hostCharsetName);
204                 }
205             } catch (UnsupportedEncodingException e) {
206                 throw new CobolConversionException(
207                         "UnsupportedEncodingException:" + e.getMessage());
208             }
209         }
210 
211         /*
212          * If required, fail if target alphanumeric data item is too short,
213          * otherwise just truncate.
214          */
215         if (failOnOverflow && hostSource.length > cobolByteLength) {
216             throw new CobolConversionException(
217                     "Java string content too long for target COBOL alphanumeric data item");
218         }
219 
220         /*
221          * The target host element might be larger than the converted java
222          * String and might have to be right or left justified.
223          */
224         int leftPadIndex = offset;
225         int rightPadIndex = lastOffset;
226 
227         if (hostSource.length < cobolByteLength) {
228             if (isJustifiedRight) {
229                 leftPadIndex = offset + cobolByteLength - hostSource.length;
230             } else {
231                 rightPadIndex = lastOffset - 1 - cobolByteLength
232                         + hostSource.length;
233             }
234         }
235 
236         int iSource = 0;
237         for (int iTarget = offset; iTarget < lastOffset; iTarget++) {
238             if (iTarget < leftPadIndex || iTarget > rightPadIndex) {
239                 hostTarget[iTarget] = padChar;
240             } else {
241                 hostTarget[iTarget] = hostSource[iSource];
242                 iSource++;
243             }
244         }
245 
246         return lastOffset;
247 
248     }
249 
250     /**
251      * Check that content represents binary data.
252      * 
253      * @param javaString the java string which content must be tested
254      * @return true if content represents a valid hexadecimal string
255      */
256     private static boolean isBinaryContent(final String javaString) {
257         if (javaString != null && javaString.length() > 2
258                 && javaString.charAt(0) == '0') {
259             Matcher matcher = BINARY_CONTENT_PATTERN.matcher(javaString);
260             if (matcher.matches()) {
261                 return true;
262             }
263         }
264         return false;
265     }
266 
267     /**
268      * Converts a host character string into a Java string.
269      * 
270      * @param hostCharsetName host character set
271      * @param cobolByteLength host byte length
272      * @param hostSource source host buffer
273      * @param offset offset in source host buffer
274      * @return offset after host buffer is read
275      * @throws CobolConversionException if conversion fails
276      */
277     public static final String fromHostSingle(final String hostCharsetName,
278             final int cobolByteLength, final byte[] hostSource, final int offset)
279             throws CobolConversionException {
280 
281         String javaString = null;
282         int javaStringLength = cobolByteLength;
283 
284         /*
285          * Check that we are still within the host source range. If not,
286          * consider the host optimized its payload by truncating trailing nulls
287          * in which case, we just need to process the characters returned if
288          * any.
289          */
290         int lastOffset = offset + cobolByteLength;
291         if (lastOffset > hostSource.length) {
292             if (offset >= hostSource.length) {
293                 return javaString;
294             } else {
295                 javaStringLength = hostSource.length - offset;
296             }
297         }
298 
299         /*
300          * The Java String is obtained by translating from the host code page to
301          * the local code page.
302          */
303         try {
304             /* Trim low-values unless there is only one */
305             int i = javaStringLength;
306             while (i > 0 && (hostSource[offset + i - 1] == 0)) {
307                 i--;
308             }
309             javaStringLength = i;
310 
311             javaString = new String(hostSource, offset, javaStringLength,
312                     hostCharsetName);
313 
314             /*
315              * Some low-value characters may have slipped into the resulting
316              * string.
317              */
318             if (javaString.indexOf("\0") != -1) {
319                 javaString = javaString.replace('\0', ' ');
320             }
321 
322             // Trim trailing spaces
323             int end = javaString.length();
324             while ((end != 0)
325                     && Character.isWhitespace(javaString.charAt(end - 1))) {
326                 end--;
327             }
328 
329             return javaString.substring(0, end);
330         } catch (UnsupportedEncodingException uee) {
331             throw new CobolConversionException("UnsupportedEncodingException:"
332                     + uee.getMessage());
333         }
334 
335     }
336 
337 }