CharsetEncoderICU.java 14.2 KB
/*
 * Decompiled with CFR 0_118.
 */
package com.adobe.agl.charset;

import com.adobe.agl.charset.CharsetCallback;
import com.adobe.agl.charset.CharsetICU;
import com.adobe.agl.impl.Assert;
import com.adobe.agl.lang.UCharacter;
import com.adobe.agl.text.UTF16;

import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;

public abstract class CharsetEncoderICU
extends CharsetEncoder {
    static final char MISSING_CHAR_MARKER = '\uffff';
    byte[] errorBuffer = new byte[30];
    int errorBufferLength = 0;
    int fromUnicodeStatus;
    int fromUChar32;
    boolean useSubChar1;
    boolean useFallback;
    static final int EXT_MAX_UCHARS = 19;
    int preFromUFirstCP;
    char[] preFromUArray = new char[19];
    int preFromUBegin;
    int preFromULength;
    char[] invalidUCharBuffer = new char[2];
    int invalidUCharLength;
    Object fromUContext;
    private CharsetCallback.Encoder onUnmappableInput = CharsetCallback.FROM_U_CALLBACK_STOP;
    private CharsetCallback.Encoder onMalformedInput = CharsetCallback.FROM_U_CALLBACK_STOP;
    CharsetCallback.Encoder fromCharErrorBehaviour;
    private static final CharBuffer EMPTY = CharBuffer.allocate(0);

    CharsetEncoderICU(CharsetICU cs, byte[] replacement) {
        super(cs, (cs.minBytesPerChar + cs.maxBytesPerChar) / 2, cs.maxBytesPerChar, replacement);
        this.fromCharErrorBehaviour = new CharsetCallback.Encoder(){

            public CoderResult call(CharsetEncoderICU encoder, Object context, CharBuffer source, ByteBuffer target, IntBuffer offsets, char[] buffer, int length, int cp, CoderResult cr) {
                if (cr.isUnmappable()) {
                    return CharsetEncoderICU.this.onUnmappableInput.call(encoder, context, source, target, offsets, buffer, length, cp, cr);
                }
                return CharsetEncoderICU.this.onMalformedInput.call(encoder, context, source, target, offsets, buffer, length, cp, cr);
            }
        };
    }

    public boolean isFallbackUsed() {
        return this.useFallback;
    }

    public void setFallbackUsed(boolean usesFallback) {
        this.useFallback = usesFallback;
    }

    final boolean isFromUUseFallback(int c) {
        return this.useFallback || UCharacter.getType(c) == 17;
    }

    static final boolean isFromUUseFallback(boolean iUseFallback, int c) {
        return iUseFallback || UCharacter.getType(c) == 17;
    }

    protected void implOnMalformedInput(CodingErrorAction newAction) {
        this.onMalformedInput = CharsetEncoderICU.getCallback(newAction);
    }

    protected void implOnUnmappableCharacter(CodingErrorAction newAction) {
        this.onUnmappableInput = CharsetEncoderICU.getCallback(newAction);
    }

    public final void setFromUCallback(CoderResult err, CharsetCallback.Encoder newCallback, Object newContext) {
        if (err.isMalformed()) {
            this.onMalformedInput = newCallback;
        } else if (err.isUnmappable()) {
            this.onUnmappableInput = newCallback;
        }
        if (this.fromUContext == null || !this.fromUContext.equals(newContext)) {
            this.setFromUContext(newContext);
        }
    }

    public final void setFromUContext(Object newContext) {
        this.fromUContext = newContext;
    }

    private static CharsetCallback.Encoder getCallback(CodingErrorAction action) {
        if (action == CodingErrorAction.REPLACE) {
            return CharsetCallback.FROM_U_CALLBACK_SUBSTITUTE;
        }
        if (action == CodingErrorAction.IGNORE) {
            return CharsetCallback.FROM_U_CALLBACK_SKIP;
        }
        return CharsetCallback.FROM_U_CALLBACK_STOP;
    }

    protected CoderResult implFlush(ByteBuffer out) {
        return this.encode(EMPTY, out, null, true);
    }

    protected void implReset() {
        this.errorBufferLength = 0;
        this.fromUnicodeStatus = 0;
        this.fromUChar32 = 0;
        this.fromUnicodeReset();
    }

    private void fromUnicodeReset() {
        this.preFromUBegin = 0;
        this.preFromUFirstCP = -1;
        this.preFromULength = 0;
    }

    protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
        if (!in.hasRemaining() && this.errorBufferLength == 0) {
            this.fromUChar32 = 0;
            return CoderResult.UNDERFLOW;
        }
        in.position(in.position() + this.fromUCountPending());
        CoderResult ret = this.encode(in, out, null, false);
        this.setSourcePosition(in);
        return ret;
    }

    abstract CoderResult encodeLoop(CharBuffer var1, ByteBuffer var2, IntBuffer var3, boolean var4);

    final CoderResult encode(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush) {
        if (target == null || source == null) {
            throw new IllegalArgumentException();
        }
        if (this.errorBufferLength > 0) {
            byte[] overflowArray = this.errorBuffer;
            int length = this.errorBufferLength;
            int i = 0;
            do {
                if (target.remaining() == 0) {
                    int j = 0;
                    do {
                        overflowArray[j++] = overflowArray[i++];
                    } while (i < length);
                    this.errorBufferLength = (byte)j;
                    return CoderResult.OVERFLOW;
                }
                target.put(overflowArray[i++]);
                if (offsets == null) continue;
                offsets.put(-1);
            } while (i < length);
            this.errorBufferLength = 0;
        }
        if (!flush && source.remaining() == 0 && this.preFromULength >= 0) {
            return CoderResult.UNDERFLOW;
        }
        return this.fromUnicodeWithCallback(source, target, offsets, flush);
    }

    /*
     * Unable to fully structure code
     * Enabled aggressive block sorting
     * Lifted jumps to return sites
     */
    final CoderResult fromUnicodeWithCallback(CharBuffer source, ByteBuffer target, IntBuffer offsets, boolean flush) {
        replayArray = CharBuffer.allocate(19);
        replayArrayIndex = 0;
        cr = CoderResult.UNDERFLOW;
        sourceIndex = 0;
        if (this.preFromULength >= 0) {
            realSource = null;
            realFlush = false;
        } else {
            realSource = source;
            realFlush = flush;
            replayArray.put(this.preFromUArray, 0, - this.preFromULength);
            source = replayArray;
            source.position(replayArrayIndex);
            source.limit(replayArrayIndex - this.preFromULength);
            flush = false;
            this.preFromULength = 0;
        }
        block0 : do {
            converterSawEndOfInput = (cr = this.encodeLoop(source, target, offsets, flush)).isUnderflow() != false && flush != false && source.remaining() == 0 && this.fromUChar32 == 0;
            calledCallback = false;
            errorInputLength = 0;
            do {
                if (this.preFromULength < 0) {
                    if (realSource == null) {
                        realSource = source;
                        realFlush = flush;
                        replayArray.put(this.preFromUArray, 0, - this.preFromULength);
                        source = replayArray;
                        source.position(replayArrayIndex);
                        source.limit(replayArrayIndex - this.preFromULength);
                        flush = false;
                        if ((sourceIndex += this.preFromULength) < 0) {
                            sourceIndex = -1;
                        }
                        this.preFromULength = 0;
                    } else {
                        Assert.assrt(realSource == null);
                    }
                }
                sBufferIndex = source.position();
                if (cr.isUnderflow()) {
                    if (sBufferIndex < source.limit()) continue block0;
                    if (realSource != null) {
                        source = realSource;
                        flush = realFlush;
                        sourceIndex = source.position();
                        realSource = null;
                        continue block0;
                    }
                    if (flush && this.fromUChar32 != 0) {
                        cr = CoderResult.malformedForLength(1);
                        calledCallback = false;
                    } else {
                        if (flush == false) return cr;
                        if (converterSawEndOfInput) ** break;
                        continue block0;
                        this.implReset();
                        return cr;
                    }
                }
                if (calledCallback || cr.isOverflow() || !cr.isMalformed() && !cr.isUnmappable()) {
                    if (realSource == null) return cr;
                    length = source.remaining();
                    if (length > 0) {
                        source.get(this.preFromUArray, 0, length);
                        this.preFromULength = (byte)(- length);
                    }
                    source = realSource;
                    flush = realFlush;
                    return cr;
                }
                codePoint = this.fromUChar32;
                this.invalidUCharLength = errorInputLength = UTF16.append(this.invalidUCharBuffer, 0, this.fromUChar32);
                this.fromUChar32 = 0;
                cr = this.fromCharErrorBehaviour.call(this, this.fromUContext, source, target, offsets, this.invalidUCharBuffer, this.invalidUCharLength, codePoint, cr);
                calledCallback = true;
            } while (true);
            break;
        } while (true);
    }

    public boolean canEncode(int[] codepoint) {
        return true;
    }

    public boolean isLegalReplacement(byte[] repl) {
        return true;
    }

    static final CoderResult fromUWriteBytes(CharsetEncoderICU cnv, byte[] bytesArray, int bytesBegin, int bytesLength, ByteBuffer out, IntBuffer offsets, int sourceIndex) {
        int obl = bytesLength;
        CoderResult cr = CoderResult.UNDERFLOW;
        int bytesLimit = bytesBegin + bytesLength;
        try {
            while (bytesBegin < bytesLimit) {
                out.put(bytesArray[bytesBegin]);
                ++bytesBegin;
            }
            bytesLength = 0;
        }
        catch (BufferOverflowException ex) {
            cr = CoderResult.OVERFLOW;
        }
        if (offsets != null) {
            while (obl > bytesLength) {
                offsets.put(sourceIndex);
                --obl;
            }
        }
        cnv.errorBufferLength = bytesLimit - bytesBegin;
        if (cnv.errorBufferLength > 0) {
            int index = 0;
            while (bytesBegin < bytesLimit) {
                cnv.errorBuffer[index++] = bytesArray[bytesBegin++];
            }
            cr = CoderResult.OVERFLOW;
        }
        return cr;
    }

    int fromUCountPending() {
        if (this.preFromULength > 0) {
            return UTF16.getCharCount(this.preFromUFirstCP) + this.preFromULength;
        }
        if (this.preFromULength < 0) {
            return - this.preFromULength;
        }
        if (this.fromUChar32 > 0) {
            return 1;
        }
        if (this.preFromUFirstCP > 0) {
            return UTF16.getCharCount(this.preFromUFirstCP);
        }
        return 0;
    }

    private final void setSourcePosition(CharBuffer source) {
        source.position(source.position() - this.fromUCountPending());
    }

    CoderResult cbFromUWriteSub(CharsetEncoderICU encoder, CharBuffer source, ByteBuffer target, IntBuffer offsets) {
        CharsetICU cs = (CharsetICU)encoder.charset();
        byte[] sub = encoder.replacement();
        if (cs.subChar1 != 0 && encoder.invalidUCharBuffer[0] <= '\u00ff') {
            return CharsetEncoderICU.fromUWriteBytes(encoder, new byte[]{cs.subChar1}, 0, 1, target, offsets, source.position());
        }
        return CharsetEncoderICU.fromUWriteBytes(encoder, sub, 0, sub.length, target, offsets, source.position());
    }

    CoderResult cbFromUWriteUChars(CharsetEncoderICU encoder, CharBuffer source, ByteBuffer target, IntBuffer offsets) {
        CoderResult cr = CoderResult.UNDERFLOW;
        int offsetIndex = source.position();
        cr = encoder.encode(source, target, null, false);
        if (offsets != null) {
            for (int oldTargetPosition = target.position(); target.position() != oldTargetPosition; ++oldTargetPosition) {
                offsets.put(offsetIndex);
            }
        }
        if (cr.isOverflow()) {
            int errBuffLen = encoder.errorBufferLength;
            ByteBuffer newTarget = ByteBuffer.wrap(encoder.errorBuffer);
            newTarget.position(errBuffLen);
            encoder.errorBufferLength = 0;
            encoder.encode(source, newTarget, null, false);
            encoder.errorBuffer = newTarget.array();
            encoder.errorBufferLength = newTarget.position();
        }
        return cr;
    }

    final CoderResult handleSurrogates(CharBuffer source, char lead) {
        if (!UTF16.isLeadSurrogate(lead)) {
            this.fromUChar32 = lead;
            return CoderResult.malformedForLength(1);
        }
        if (!source.hasRemaining()) {
            this.fromUChar32 = lead;
            return CoderResult.UNDERFLOW;
        }
        char trail = source.get();
        if (!UTF16.isTrailSurrogate(trail)) {
            this.fromUChar32 = lead;
            source.position(source.position() - 1);
            return CoderResult.malformedForLength(1);
        }
        this.fromUChar32 = UCharacter.getCodePoint(lead, trail);
        return null;
    }

    final CoderResult handleSurrogates(char[] sourceArray, int sourceIndex, int sourceLimit, char lead) {
        if (!UTF16.isLeadSurrogate(lead)) {
            this.fromUChar32 = lead;
            return CoderResult.malformedForLength(1);
        }
        if (sourceIndex >= sourceLimit) {
            this.fromUChar32 = lead;
            return CoderResult.UNDERFLOW;
        }
        char trail = sourceArray[sourceIndex];
        if (!UTF16.isTrailSurrogate(trail)) {
            this.fromUChar32 = lead;
            return CoderResult.malformedForLength(1);
        }
        this.fromUChar32 = UCharacter.getCodePoint(lead, trail);
        return null;
    }

}