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