• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3 /**
4  *******************************************************************************
5  * Copyright (C) 2006-2013, International Business Machines Corporation and    *
6  * others. All Rights Reserved.                                                *
7  *******************************************************************************
8  *
9  *******************************************************************************
10  */
11 
12 package com.ibm.icu.charset;
13 
14 import java.nio.BufferOverflowException;
15 import java.nio.ByteBuffer;
16 import java.nio.CharBuffer;
17 import java.nio.IntBuffer;
18 import java.nio.charset.CharsetEncoder;
19 import java.nio.charset.CoderResult;
20 import java.nio.charset.CodingErrorAction;
21 
22 import com.ibm.icu.impl.Assert;
23 import com.ibm.icu.lang.UCharacter;
24 import com.ibm.icu.text.UTF16;
25 
26 /**
27  * An abstract class that provides framework methods of decoding operations for concrete
28  * subclasses.
29  * In the future this class will contain API that will implement converter semantics of ICU4C.
30  * @stable ICU 3.6
31  */
32 public abstract class CharsetEncoderICU extends CharsetEncoder {
33 
34     /* this is used in fromUnicode DBCS tables as an "unassigned" marker */
35     static final char MISSING_CHAR_MARKER = '\uFFFF';
36 
37     byte[] errorBuffer = new byte[30];
38 
39     int errorBufferLength = 0;
40 
41     /** these are for encodeLoopICU */
42     int fromUnicodeStatus;
43 
44     int fromUChar32;
45 
46     boolean useSubChar1;
47 
48     boolean useFallback;
49 
50     /* maximum number of indexed UChars */
51     static final int EXT_MAX_UCHARS = 19;
52 
53     /* store previous UChars/chars to continue partial matches */
54     int preFromUFirstCP; /* >=0: partial match */
55 
56     char[] preFromUArray = new char[EXT_MAX_UCHARS];
57 
58     int preFromUBegin;
59 
60     int preFromULength; /* negative: replay */
61 
62     char[] invalidUCharBuffer = new char[2];
63 
64     int invalidUCharLength;
65 
66     Object fromUContext;
67 
68     private CharsetCallback.Encoder onUnmappableInput = CharsetCallback.FROM_U_CALLBACK_STOP;
69 
70     private CharsetCallback.Encoder onMalformedInput = CharsetCallback.FROM_U_CALLBACK_STOP;
71 
72     CharsetCallback.Encoder fromCharErrorBehaviour = new CharsetCallback.Encoder() {
73         @Override
74         public CoderResult call(CharsetEncoderICU encoder, Object context,
75                 CharBuffer source, ByteBuffer target, IntBuffer offsets,
76                 char[] buffer, int length, int cp, CoderResult cr) {
77             if (cr.isUnmappable()) {
78                 return onUnmappableInput.call(encoder, context, source, target,
79                         offsets, buffer, length, cp, cr);
80             } else /* if (cr.isMalformed()) */ {
81                 return onMalformedInput.call(encoder, context, source, target,
82                         offsets, buffer, length, cp, cr);
83             }
84             // return CharsetCallback.FROM_U_CALLBACK_STOP.call(encoder, context, source, target, offsets, buffer, length, cp, cr);
85 
86         }
87     };
88 
89     /*
90      * Construcs a new encoder for the given charset
91      *
92      * @param cs
93      *            for which the decoder is created
94      * @param replacement
95      *            the substitution bytes
96      */
CharsetEncoderICU(CharsetICU cs, byte[] replacement)97     CharsetEncoderICU(CharsetICU cs, byte[] replacement) {
98         super(cs, (cs.minBytesPerChar + cs.maxBytesPerChar) / 2,
99                 cs.maxBytesPerChar, replacement);
100     }
101 
102     /**
103      * Is this Encoder allowed to use fallbacks? A fallback mapping is a mapping
104      * that will convert a Unicode codepoint sequence to a byte sequence, but
105      * the encoded byte sequence will round trip convert to a different
106      * Unicode codepoint sequence.
107      * @return true if the converter uses fallback, false otherwise.
108      * @stable ICU 3.8
109      */
isFallbackUsed()110     public boolean isFallbackUsed() {
111         return useFallback;
112     }
113 
114     /**
115      * Sets whether this Encoder can use fallbacks?
116      * @param usesFallback true if the user wants the converter to take
117      *  advantage of the fallback mapping, false otherwise.
118      * @stable ICU 3.8
119      */
setFallbackUsed(boolean usesFallback)120     public void setFallbackUsed(boolean usesFallback) {
121         useFallback = usesFallback;
122     }
123 
124     /*
125      * Use fallbacks from Unicode to codepage when useFallback or for private-use code points
126      * @param c A codepoint
127      */
isFromUUseFallback(int c)128     final boolean isFromUUseFallback(int c) {
129         return (useFallback) || isUnicodePrivateUse(c);
130     }
131 
132     /**
133      * Use fallbacks from Unicode to codepage when useFallback or for private-use code points
134      */
isFromUUseFallback(boolean iUseFallback, int c)135     static final boolean isFromUUseFallback(boolean iUseFallback, int c) {
136         return (iUseFallback) || isUnicodePrivateUse(c);
137     }
138 
isUnicodePrivateUse(int c)139     private static final boolean isUnicodePrivateUse(int c) {
140         // First test for U+E000 to optimize for the most common characters.
141         return c >= 0xE000 && (c <= 0xF8FF ||
142                 c >= 0xF0000 && (c <= 0xFFFFD ||
143                 (c >= 0x100000 && c <= 0x10FFFD)));
144     }
145 
146     /**
147      * Sets the action to be taken if an illegal sequence is encountered
148      *
149      * @param newAction
150      *            action to be taken
151      * @exception IllegalArgumentException
152      * @stable ICU 3.6
153      */
154     @Override
implOnMalformedInput(CodingErrorAction newAction)155     protected void implOnMalformedInput(CodingErrorAction newAction) {
156         onMalformedInput = getCallback(newAction);
157     }
158 
159     /**
160      * Sets the action to be taken if an illegal sequence is encountered
161      *
162      * @param newAction
163      *            action to be taken
164      * @exception IllegalArgumentException
165      * @stable ICU 3.6
166      */
167     @Override
implOnUnmappableCharacter(CodingErrorAction newAction)168     protected void implOnUnmappableCharacter(CodingErrorAction newAction) {
169         onUnmappableInput = getCallback(newAction);
170     }
171 
172     /**
173      * Sets the callback encoder method and context to be used if an illegal sequence is encountered.
174      * You would normally call this twice to set both the malform and unmappable error. In this case,
175      * newContext should remain the same since using a different newContext each time will negate the last
176      * one used.
177      * @param err CoderResult
178      * @param newCallback CharsetCallback.Encoder
179      * @param newContext Object
180      * @stable ICU 4.0
181      */
setFromUCallback(CoderResult err, CharsetCallback.Encoder newCallback, Object newContext)182     public final void setFromUCallback(CoderResult err, CharsetCallback.Encoder newCallback, Object newContext) {
183         if (err.isMalformed()) {
184             onMalformedInput = newCallback;
185         } else if (err.isUnmappable()) {
186             onUnmappableInput = newCallback;
187         } else {
188             /* Error: Only malformed and unmappable are handled. */
189         }
190 
191         if (fromUContext == null || !fromUContext.equals(newContext)) {
192             setFromUContext(newContext);
193         }
194     }
195 
196     /**
197      * Sets fromUContext used in callbacks.
198      *
199      * @param newContext Object
200      * @exception IllegalArgumentException The object is an illegal argument for UContext.
201      * @stable ICU 4.0
202      */
setFromUContext(Object newContext)203     public final void setFromUContext(Object newContext) {
204         fromUContext = newContext;
205     }
206 
getCallback(CodingErrorAction action)207     private static CharsetCallback.Encoder getCallback(CodingErrorAction action) {
208         if (action == CodingErrorAction.REPLACE) {
209             return CharsetCallback.FROM_U_CALLBACK_SUBSTITUTE;
210         } else if (action == CodingErrorAction.IGNORE) {
211             return CharsetCallback.FROM_U_CALLBACK_SKIP;
212         } else /* if (action == CodingErrorAction.REPORT) */ {
213             return CharsetCallback.FROM_U_CALLBACK_STOP;
214         }
215     }
216 
217     private static final CharBuffer EMPTY = CharBuffer.allocate(0);
218 
219     /**
220      * Flushes any characters saved in the converter's internal buffer and
221      * resets the converter.
222      * @param out action to be taken
223      * @return result of flushing action and completes the decoding all input.
224      *         Returns CoderResult.UNDERFLOW if the action succeeds.
225      * @stable ICU 3.6
226      */
227     @Override
implFlush(ByteBuffer out)228     protected CoderResult implFlush(ByteBuffer out) {
229         return encode(EMPTY, out, null, true);
230     }
231 
232     /**
233      * Resets the from Unicode mode of converter
234      * @stable ICU 3.6
235      */
236     @Override
implReset()237     protected void implReset() {
238         errorBufferLength = 0;
239         fromUnicodeStatus = 0;
240         fromUChar32 = 0;
241         fromUnicodeReset();
242     }
243 
fromUnicodeReset()244     private void fromUnicodeReset() {
245         preFromUBegin = 0;
246         preFromUFirstCP = UConverterConstants.U_SENTINEL;
247         preFromULength = 0;
248     }
249 
250     /**
251      * Encodes one or more chars. The default behaviour of the
252      * converter is stop and report if an error in input stream is encountered.
253      * To set different behaviour use @see CharsetEncoder.onMalformedInput()
254      * @param in buffer to decode
255      * @param out buffer to populate with decoded result
256      * @return result of decoding action. Returns CoderResult.UNDERFLOW if the decoding
257      *         action succeeds or more input is needed for completing the decoding action.
258      * @stable ICU 3.6
259      */
260     @Override
encodeLoop(CharBuffer in, ByteBuffer out)261     protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
262         if (!in.hasRemaining() && this.errorBufferLength == 0) { // make sure the errorBuffer is empty
263             // The Java framework should have already substituted what was left.
264             fromUChar32 = 0;
265             //fromUnicodeReset();
266             return CoderResult.UNDERFLOW;
267         }
268         in.position(in.position() + fromUCountPending());
269         /* do the conversion */
270         CoderResult ret = encode(in, out, null, false);
271         setSourcePosition(in);
272         /* No need to reset to keep the proper state of the encoder.
273          if (ret.isUnderflow() && in.hasRemaining()) {
274             // The Java framework is going to substitute what is left.
275             //fromUnicodeReset();
276         } */
277         return ret;
278     }
279 
280     /*
281      * Implements ICU semantics of buffer management
282      * @param source
283      * @param target
284      * @param offsets
285      * @return A CoderResult object that contains the error result when an error occurs.
286      */
encodeLoop(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush)287     abstract CoderResult encodeLoop(CharBuffer source, ByteBuffer target,
288             IntBuffer offsets, boolean flush);
289 
290     /*
291      * Implements ICU semantics for encoding the buffer
292      * @param source The input character buffer
293      * @param target The output byte buffer
294      * @param offsets
295      * @param flush true if, and only if, the invoker can provide no
296      *  additional input bytes beyond those in the given buffer.
297      * @return A CoderResult object that contains the error result when an error occurs.
298      */
encode(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush)299     final CoderResult encode(CharBuffer source, ByteBuffer target,
300             IntBuffer offsets, boolean flush) {
301 
302         /* check parameters */
303         if (target == null || source == null) {
304             throw new IllegalArgumentException();
305         }
306 
307         /*
308          * Make sure that the buffer sizes do not exceed the number range for
309          * int32_t because some functions use the size (in units or bytes)
310          * rather than comparing pointers, and because offsets are int32_t values.
311          *
312          * size_t is guaranteed to be unsigned and large enough for the job.
313          *
314          * Return with an error instead of adjusting the limits because we would
315          * not be able to maintain the semantics that either the source must be
316          * consumed or the target filled (unless an error occurs).
317          * An adjustment would be targetLimit=t+0x7fffffff; for example.
318          */
319 
320         /* flush the target overflow buffer */
321         if (errorBufferLength > 0) {
322             byte[] overflowArray;
323             int i, length;
324 
325             overflowArray = errorBuffer;
326             length = errorBufferLength;
327             i = 0;
328             do {
329                 if (target.remaining() == 0) {
330                     /* the overflow buffer contains too much, keep the rest */
331                     int j = 0;
332 
333                     do {
334                         overflowArray[j++] = overflowArray[i++];
335                     } while (i < length);
336 
337                     errorBufferLength = (byte) j;
338                     return CoderResult.OVERFLOW;
339                 }
340 
341                 /* copy the overflow contents to the target */
342                 target.put(overflowArray[i++]);
343                 if (offsets != null) {
344                     offsets.put(-1); /* no source index available for old output */
345                 }
346             } while (i < length);
347 
348             /* the overflow buffer is completely copied to the target */
349             errorBufferLength = 0;
350         }
351 
352         if (!flush && source.remaining() == 0 && preFromULength >= 0) {
353             /* the overflow buffer is emptied and there is no new input: we are done */
354             return CoderResult.UNDERFLOW;
355         }
356 
357         /*
358          * Do not simply return with a buffer overflow error if
359          * !flush && t==targetLimit
360          * because it is possible that the source will not generate any output.
361          * For example, the skip callback may be called;
362          * it does not output anything.
363          */
364 
365         return fromUnicodeWithCallback(source, target, offsets, flush);
366 
367     }
368 
369     /*
370      * Implementation note for m:n conversions
371      *
372      * While collecting source units to find the longest match for m:n conversion,
373      * some source units may need to be stored for a partial match.
374      * When a second buffer does not yield a match on all of the previously stored
375      * source units, then they must be "replayed", i.e., fed back into the converter.
376      *
377      * The code relies on the fact that replaying will not nest -
378      * converting a replay buffer will not result in a replay.
379      * This is because a replay is necessary only after the _continuation_ of a
380      * partial match failed, but a replay buffer is converted as a whole.
381      * It may result in some of its units being stored again for a partial match,
382      * but there will not be a continuation _during_ the replay which could fail.
383      *
384      * It is conceivable that a callback function could call the converter
385      * recursively in a way that causes another replay to be stored, but that
386      * would be an error in the callback function.
387      * Such violations will cause assertion failures in a debug build,
388      * and wrong output, but they will not cause a crash.
389      */
fromUnicodeWithCallback(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush)390     final CoderResult fromUnicodeWithCallback(CharBuffer source,
391             ByteBuffer target, IntBuffer offsets, boolean flush) {
392         int sBufferIndex;
393         int sourceIndex;
394         int errorInputLength;
395         boolean converterSawEndOfInput, calledCallback;
396 
397         /* variables for m:n conversion */
398         CharBuffer replayArray = CharBuffer.allocate(EXT_MAX_UCHARS);
399         int replayArrayIndex = 0;
400         CharBuffer realSource;
401         boolean realFlush;
402 
403         CoderResult cr = CoderResult.UNDERFLOW;
404 
405         /* get the converter implementation function */
406         sourceIndex = 0;
407 
408         if (preFromULength >= 0) {
409             /* normal mode */
410             realSource = null;
411             realFlush = false;
412         } else {
413             /*
414              * Previous m:n conversion stored source units from a partial match
415              * and failed to consume all of them.
416              * We need to "replay" them from a temporary buffer and convert them first.
417              */
418             realSource = source;
419             realFlush = flush;
420 
421             //UConverterUtility.uprv_memcpy(replayArray, replayArrayIndex, preFromUArray, 0, -preFromULength*UMachine.U_SIZEOF_UCHAR);
422             replayArray.put(preFromUArray, 0, -preFromULength);
423             source = replayArray;
424             source.position(replayArrayIndex);
425             source.limit(replayArrayIndex - preFromULength); //preFromULength is negative, see declaration
426             flush = false;
427 
428             preFromULength = 0;
429         }
430 
431         /*
432          * loop for conversion and error handling
433          *
434          * loop {
435          *   convert
436          *   loop {
437          *     update offsets
438          *     handle end of input
439          *     handle errors/call callback
440          *   }
441          * }
442          */
443         for (;;) {
444             /* convert */
445             cr = encodeLoop(source, target, offsets, flush);
446             /*
447              * set a flag for whether the converter
448              * successfully processed the end of the input
449              *
450              * need not check cnv.preFromULength==0 because a replay (<0) will cause
451              * s<sourceLimit before converterSawEndOfInput is checked
452              */
453             converterSawEndOfInput = (cr.isUnderflow() && flush
454                     && source.remaining() == 0 && fromUChar32 == 0);
455 
456             /* no callback called yet for this iteration */
457             calledCallback = false;
458 
459             /* no sourceIndex adjustment for conversion, only for callback output */
460             errorInputLength = 0;
461 
462             /*
463              * loop for offsets and error handling
464              *
465              * iterates at most 3 times:
466              * 1. to clean up after the conversion function
467              * 2. after the callback
468              * 3. after the callback again if there was truncated input
469              */
470             for (;;) {
471                 /* update offsets if we write any */
472                 /* Currently offsets are not being used in ICU4J */
473                 /* if (offsets != null) {
474                     int length = target.remaining();
475                     if (length > 0) {
476 
477                         /*
478                          * if a converter handles offsets and updates the offsets
479                          * pointer at the end, then offset should not change
480                          * here;
481                          * however, some converters do not handle offsets at all
482                          * (sourceIndex<0) or may not update the offsets pointer
483                          */
484                  /*       offsets.position(offsets.position() + length);
485                     }
486 
487                     if (sourceIndex >= 0) {
488                         sourceIndex += (int) (source.position());
489                     }
490                 } */
491 
492                 if (preFromULength < 0) {
493                     /*
494                      * switch the source to new replay units (cannot occur while replaying)
495                      * after offset handling and before end-of-input and callback handling
496                      */
497                     if (realSource == null) {
498                         realSource = source;
499                         realFlush = flush;
500 
501                         //UConverterUtility.uprv_memcpy(replayArray, replayArrayIndex, preFromUArray, 0, -preFromULength*UMachine.U_SIZEOF_UCHAR);
502                         replayArray.put(preFromUArray, 0, -preFromULength);
503 
504                         source = replayArray;
505                         source.position(replayArrayIndex);
506                         source.limit(replayArrayIndex - preFromULength);
507                         flush = false;
508                         if ((sourceIndex += preFromULength) < 0) {
509                             sourceIndex = -1;
510                         }
511 
512                         preFromULength = 0;
513                     } else {
514                         /* see implementation note before _fromUnicodeWithCallback() */
515                         //agljport:todo U_ASSERT(realSource==NULL);
516                         Assert.assrt(realSource == null);
517                     }
518                 }
519 
520                 /* update pointers */
521                 sBufferIndex = source.position();
522                 if (cr.isUnderflow()) {
523                     if (sBufferIndex < source.limit()) {
524                         /*
525                          * continue with the conversion loop while there is still input left
526                          * (continue converting by breaking out of only the inner loop)
527                          */
528                         break;
529                     } else if (realSource != null) {
530                         /* switch back from replaying to the real source and continue */
531                         source = realSource;
532                         flush = realFlush;
533                         sourceIndex = source.position();
534                         realSource = null;
535                         break;
536                     } else if (flush && fromUChar32 != 0) {
537                         /*
538                          * the entire input stream is consumed
539                          * and there is a partial, truncated input sequence left
540                          */
541 
542                         /* inject an error and continue with callback handling */
543                         //err[0]=ErrorCode.U_TRUNCATED_CHAR_FOUND;
544                         cr = CoderResult.malformedForLength(1);
545                         calledCallback = false; /* new error condition */
546                     } else {
547                         /* input consumed */
548                         if (flush) {
549                             /*
550                              * return to the conversion loop once more if the flush
551                              * flag is set and the conversion function has not
552                              * successfully processed the end of the input yet
553                              *
554                              * (continue converting by breaking out of only the inner loop)
555                              */
556                             if (!converterSawEndOfInput) {
557                                 break;
558                             }
559 
560                             /* reset the converter without calling the callback function */
561                             implReset();
562                         }
563 
564                         /* done successfully */
565                         return cr;
566                     }
567                 }
568 
569                 /*U_FAILURE(*err) */
570                 {
571 
572                     if (calledCallback || cr.isOverflow()
573                             || (!cr.isMalformed() && !cr.isUnmappable())) {
574                         /*
575                          * the callback did not or cannot resolve the error:
576                          * set output pointers and return
577                          *
578                          * the check for buffer overflow is redundant but it is
579                          * a high-runner case and hopefully documents the intent
580                          * well
581                          *
582                          * if we were replaying, then the replay buffer must be
583                          * copied back into the UConverter
584                          * and the real arguments must be restored
585                          */
586                         if (realSource != null) {
587                             int length;
588 
589                             //agljport:todo U_ASSERT(cnv.preFromULength==0);
590 
591                             length = source.remaining();
592                             if (length > 0) {
593                                 //UConverterUtility.uprv_memcpy(preFromUArray, 0, sourceArray, pArgs.sourceBegin, length*UMachine.U_SIZEOF_UCHAR);
594                                 source.get(preFromUArray, 0, length);
595                                 preFromULength = (byte) -length;
596                             }
597                         }
598                         return cr;
599                     }
600                 }
601 
602                 /* callback handling */
603                 {
604                     int codePoint;
605 
606                     /* get and write the code point */
607                     codePoint = fromUChar32;
608                     errorInputLength = UTF16.append(invalidUCharBuffer, 0,
609                             fromUChar32);
610                     invalidUCharLength = errorInputLength;
611 
612                     /* set the converter state to deal with the next character */
613                     fromUChar32 = 0;
614 
615                     /* call the callback function */
616                     cr = fromCharErrorBehaviour.call(this, fromUContext,
617                             source, target, offsets, invalidUCharBuffer,
618                             invalidUCharLength, codePoint, cr);
619                 }
620 
621                 /*
622                  * loop back to the offset handling
623                  *
624                  * this flag will indicate after offset handling
625                  * that a callback was called;
626                  * if the callback did not resolve the error, then we return
627                  */
628                 calledCallback = true;
629             }
630         }
631     }
632 
633     /*
634      * Ascertains if a given Unicode code point (32bit value for handling surrogates)
635      * can be converted to the target encoding. If the caller wants to test if a
636      * surrogate pair can be converted to target encoding then the
637      * responsibility of assembling the int value lies with the caller.
638      * For assembling a code point the caller can use UTF16 class of ICU4J and do something like:
639      * <pre>
640      *  while(i<mySource.length){
641      *      if(UTF16.isLeadSurrogate(mySource[i])&& i+1< mySource.length){
642      *          if(UTF16.isTrailSurrogate(mySource[i+1])){
643      *              int temp = UTF16.charAt(mySource,i,i+1,0);
644      *              if(!((CharsetEncoderICU) myConv).canEncode(temp)){
645      *                  passed=false;
646      *              }
647      *              i++;
648      *              i++;
649      *          }
650      *      }
651      *  }
652      * </pre>
653      * or
654      * <pre>
655      *  String src = new String(mySource);
656      *  int i,codepoint;
657      *  boolean passed = false;
658      *  while(i<src.length()){
659      *      codepoint = UTF16.charAt(src,i);
660      *      i+= (codepoint>0xfff)? 2:1;
661      *      if(!(CharsetEncoderICU) myConv).canEncode(codepoint)){
662      *          passed = false;
663      *      }
664      *  }
665      * </pre>
666      *
667      * @param codepoint Unicode code point as int value
668      * @return true if a character can be converted
669      */
670     /* TODO This is different from Java's canEncode(char) API.
671      * ICU's API should implement getUnicodeSet,
672      * and override canEncode(char) which queries getUnicodeSet.
673      * The getUnicodeSet should return a frozen UnicodeSet or use a fillin parameter, like ICU4C.
674      */
675     /*public boolean canEncode(int codepoint) {
676         return true;
677     }*/
678     /**
679      * Overrides super class method
680      * @stable ICU 3.6
681      */
682     @Override
isLegalReplacement(byte[] repl)683     public boolean isLegalReplacement(byte[] repl) {
684         return true;
685     }
686 
687     /*
688      * Writes out the specified output bytes to the target byte buffer or to converter internal buffers.
689      * @param cnv
690      * @param bytesArray
691      * @param bytesBegin
692      * @param bytesLength
693      * @param out
694      * @param offsets
695      * @param sourceIndex
696      * @return A CoderResult object that contains the error result when an error occurs.
697      */
fromUWriteBytes(CharsetEncoderICU cnv, byte[] bytesArray, int bytesBegin, int bytesLength, ByteBuffer out, IntBuffer offsets, int sourceIndex)698     static final CoderResult fromUWriteBytes(CharsetEncoderICU cnv,
699             byte[] bytesArray, int bytesBegin, int bytesLength, ByteBuffer out,
700             IntBuffer offsets, int sourceIndex) {
701 
702         //write bytes
703         int obl = bytesLength;
704         CoderResult cr = CoderResult.UNDERFLOW;
705         int bytesLimit = bytesBegin + bytesLength;
706         try {
707             for (; bytesBegin < bytesLimit;) {
708                 out.put(bytesArray[bytesBegin]);
709                 bytesBegin++;
710             }
711             // success
712             bytesLength = 0;
713         } catch (BufferOverflowException ex) {
714             cr = CoderResult.OVERFLOW;
715         }
716 
717         if (offsets != null) {
718             while (obl > bytesLength) {
719                 offsets.put(sourceIndex);
720                 --obl;
721             }
722         }
723         //write overflow
724         cnv.errorBufferLength = bytesLimit - bytesBegin;
725         if (cnv.errorBufferLength > 0) {
726             int index = 0;
727             while (bytesBegin < bytesLimit) {
728                 cnv.errorBuffer[index++] = bytesArray[bytesBegin++];
729             }
730             cr = CoderResult.OVERFLOW;
731         }
732         return cr;
733     }
734 
735     /*
736      * Returns the number of chars held in the converter's internal state
737      * because more input is needed for completing the conversion. This function is
738      * useful for mapping semantics of ICU's converter interface to those of iconv,
739      * and this information is not needed for normal conversion.
740      * @return The number of chars in the state. -1 if an error is encountered.
741      */
fromUCountPending()742     /*public*/int fromUCountPending() {
743         if (preFromULength > 0) {
744             return UTF16.getCharCount(preFromUFirstCP) + preFromULength;
745         } else if (preFromULength < 0) {
746             return -preFromULength;
747         } else if (fromUChar32 > 0) {
748             return 1;
749         } else if (preFromUFirstCP > 0) {
750             return UTF16.getCharCount(preFromUFirstCP);
751         }
752         return 0;
753     }
754 
755     /**
756      *
757      * @param source
758      */
setSourcePosition(CharBuffer source)759     private final void setSourcePosition(CharBuffer source) {
760 
761         // ok was there input held in the previous invocation of encodeLoop
762         // that resulted in output in this invocation?
763         source.position(source.position() - fromUCountPending());
764     }
765 
766     /*
767      * Write the codepage substitution character.
768      * Subclasses to override this method.
769      * For stateful converters, it is typically necessary to handle this
770      * specificially for the converter in order to properly maintain the state.
771      * @param source The input character buffer
772      * @param target The output byte buffer
773      * @param offsets
774      * @return A CoderResult object that contains the error result when an error occurs.
775      */
cbFromUWriteSub(CharsetEncoderICU encoder, CharBuffer source, ByteBuffer target, IntBuffer offsets)776     CoderResult cbFromUWriteSub(CharsetEncoderICU encoder, CharBuffer source,
777             ByteBuffer target, IntBuffer offsets) {
778         CharsetICU cs = (CharsetICU) encoder.charset();
779         byte[] sub = encoder.replacement();
780         if (cs.subChar1 != 0 && encoder.invalidUCharBuffer[0] <= 0xff) {
781             return CharsetEncoderICU.fromUWriteBytes(encoder,
782                     new byte[] { cs.subChar1 }, 0, 1, target, offsets, source
783                             .position());
784         } else {
785             return CharsetEncoderICU.fromUWriteBytes(encoder, sub, 0,
786                     sub.length, target, offsets, source.position());
787         }
788     }
789 
790     /*
791      * Write the characters to target.
792      * @param source The input character buffer
793      * @param target The output byte buffer
794      * @param offsets
795      * @return A CoderResult object that contains the error result when an error occurs.
796      */
cbFromUWriteUChars(CharsetEncoderICU encoder, CharBuffer source, ByteBuffer target, IntBuffer offsets)797     CoderResult cbFromUWriteUChars(CharsetEncoderICU encoder,
798             CharBuffer source, ByteBuffer target, IntBuffer offsets) {
799         CoderResult cr = CoderResult.UNDERFLOW;
800 
801         /* This is a fun one.  Recursion can occur - we're basically going to
802          * just retry shoving data through the same converter. Note, if you got
803          * here through some kind of invalid sequence, you maybe should emit a
804          * reset sequence of some kind. Since this IS an actual conversion,
805          * take care that you've changed the callback or the data, or you'll
806          * get an infinite loop.
807          */
808 
809         int oldTargetPosition = target.position();
810         int offsetIndex = source.position();
811 
812         cr = encoder.encode(source, target, null, false); /* no offsets and no flush */
813 
814         if (offsets != null) {
815             while (target.position() != oldTargetPosition) {
816                 offsets.put(offsetIndex);
817                 oldTargetPosition++;
818             }
819         }
820 
821         /* Note, if you did something like used a stop subcallback, things would get interesting.
822          * In fact, here's where we want to return the partially consumed in-source!
823          */
824         if (cr.isOverflow()) {
825             /* Overflowed target. Now, we'll write into the charErrorBuffer.
826              * It's a fixed size. If we overflow it...Hm
827              */
828 
829             /* start the new target at the first free slot in the error buffer */
830             int errBuffLen = encoder.errorBufferLength;
831             ByteBuffer newTarget = ByteBuffer.wrap(encoder.errorBuffer);
832             newTarget.position(errBuffLen); /* set the position at the end of the error buffer */
833             encoder.errorBufferLength = 0;
834 
835             encoder.encode(source, newTarget, null, false);
836 
837             encoder.errorBuffer = newTarget.array();
838             encoder.errorBufferLength = newTarget.position();
839         }
840 
841         return cr;
842     }
843 
844     /**
845      * <p>
846      * Handles a common situation where a character has been read and it may be
847      * a lead surrogate followed by a trail surrogate. This method can change
848      * the source position and will modify fromUChar32.
849      * </p>
850      *
851      * <p>
852      * If <code>null</code> is returned, then there was success in reading a
853      * surrogate pair, the codepoint is stored in <code>fromUChar32</code> and
854      * <code>fromUChar32</code> should be reset (to 0) after being read.
855      * </p>
856      *
857      * @param source
858      *            The encoding source.
859      * @param lead
860      *            A character that may be the first in a surrogate pair.
861      * @return <code>CoderResult.malformedForLength(1)</code> or
862      *         <code>CoderResult.UNDERFLOW</code> if there is a problem, or
863      *         <code>null</code> if there isn't.
864      * @see #handleSurrogates(CharBuffer, char)
865      * @see #handleSurrogates(char[], int, int, char)
866      */
handleSurrogates(CharBuffer source, char lead)867     final CoderResult handleSurrogates(CharBuffer source, char lead) {
868         if (!UTF16.isLeadSurrogate(lead)) {
869             fromUChar32 = lead;
870             return CoderResult.malformedForLength(1);
871         }
872 
873         if (!source.hasRemaining()) {
874             fromUChar32 = lead;
875             return CoderResult.UNDERFLOW;
876         }
877 
878         char trail = source.get();
879 
880         if (!UTF16.isTrailSurrogate(trail)) {
881             fromUChar32 = lead;
882             source.position(source.position() - 1);
883             return CoderResult.malformedForLength(1);
884         }
885 
886         fromUChar32 = UCharacter.getCodePoint(lead, trail);
887         return null;
888     }
889 
890     /**
891      * <p>
892      * Same as <code>handleSurrogates(CharBuffer, char)</code>, but with arrays. As an added
893      * requirement, the calling method must also increment the index if this method returns
894      * <code>null</code>.
895      * </p>
896      *
897      *
898      * @param source
899      *            The encoding source.
900      * @param lead
901      *            A character that may be the first in a surrogate pair.
902      * @return <code>CoderResult.malformedForLength(1)</code> or
903      *         <code>CoderResult.UNDERFLOW</code> if there is a problem, or <code>null</code> if
904      *         there isn't.
905      * @see #handleSurrogates(CharBuffer, char)
906      * @see #handleSurrogates(char[], int, int, char)
907      */
handleSurrogates(char[] sourceArray, int sourceIndex, int sourceLimit, char lead)908     final CoderResult handleSurrogates(char[] sourceArray, int sourceIndex,
909             int sourceLimit, char lead) {
910         if (!UTF16.isLeadSurrogate(lead)) {
911             fromUChar32 = lead;
912             return CoderResult.malformedForLength(1);
913         }
914 
915         if (sourceIndex >= sourceLimit) {
916             fromUChar32 = lead;
917             return CoderResult.UNDERFLOW;
918         }
919 
920         char trail = sourceArray[sourceIndex];
921 
922         if (!UTF16.isTrailSurrogate(trail)) {
923             fromUChar32 = lead;
924             return CoderResult.malformedForLength(1);
925         }
926 
927         fromUChar32 = UCharacter.getCodePoint(lead, trail);
928         return null;
929     }
930 
931     /**
932      * Returns the maxCharsPerByte value for the Charset that created this encoder.
933      * @return maxCharsPerByte
934      * @stable ICU 4.8
935      */
maxCharsPerByte()936     public final float maxCharsPerByte() {
937         return ((CharsetICU)(this.charset())).maxCharsPerByte;
938     }
939 
940     /**
941      * Calculates the size of a buffer for conversion from Unicode to a charset.
942      * The calculated size is guaranteed to be sufficient for this conversion.
943      *
944      * It takes into account initial and final non-character bytes that are output
945      * by some converters.
946      * It does not take into account callbacks which output more than one charset
947      * character sequence per call, like escape callbacks.
948      * The default (substitution) callback only outputs one charset character sequence.
949      *
950      * @param length Number of chars to be converted.
951      * @param maxCharSize Return value from maxBytesPerChar for the converter
952      *                    that will be used.
953      * @return Size of a buffer that will be large enough to hold the output of bytes
954      *
955      * @stable ICU 49
956      */
getMaxBytesForString(int length, int maxCharSize)957     public static int getMaxBytesForString(int length, int maxCharSize) {
958         return ((length + 10) * maxCharSize);
959     }
960 
961 }
962