mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Further implementation of nio charsets
This commit is contained in:
parent
59b2d0b21e
commit
0be769f74d
|
@ -106,6 +106,7 @@
|
|||
<argument>java.math</argument>
|
||||
<argument>java.net</argument>
|
||||
<argument>java.nio</argument>
|
||||
<argument>java.nio.charset</argument>
|
||||
<argument>java.text</argument>
|
||||
<argument>java.util</argument>
|
||||
<argument>java.util.logging</argument>
|
||||
|
|
|
@ -18,6 +18,9 @@ package org.teavm.classlib.java.nio.charset;
|
|||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.teavm.classlib.java.nio.TByteBuffer;
|
||||
import org.teavm.classlib.java.nio.TCharBuffer;
|
||||
import org.teavm.classlib.java.nio.charset.impl.UTF8Charset;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -29,12 +32,54 @@ public abstract class TCharset implements Comparable<TCharset> {
|
|||
private Set<String> aliasSet;
|
||||
|
||||
protected TCharset(String canonicalName, String[] aliases) {
|
||||
checkCanonicalName(canonicalName);
|
||||
for (String alias : aliases) {
|
||||
checkCanonicalName(alias);
|
||||
}
|
||||
this.canonicalName = canonicalName;
|
||||
this.aliases = aliases.clone();
|
||||
}
|
||||
|
||||
private static void checkCanonicalName(String name) {
|
||||
if (name.isEmpty()) {
|
||||
throw new TIllegalCharsetNameException(name);
|
||||
}
|
||||
if (!isValidCharsetStart(name.charAt(0))) {
|
||||
throw new TIllegalCharsetNameException(name);
|
||||
}
|
||||
for (int i = 1; i < name.length(); ++i) {
|
||||
char c = name.charAt(i);
|
||||
switch (c) {
|
||||
case '-':
|
||||
case '+':
|
||||
case '.':
|
||||
case ':':
|
||||
case '_':
|
||||
break;
|
||||
default:
|
||||
if (!isValidCharsetStart(c)) {
|
||||
throw new TIllegalCharsetNameException(name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValidCharsetStart(char c) {
|
||||
return c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' || c <= 'Z';
|
||||
}
|
||||
|
||||
public static TCharset forName(String charsetName) {
|
||||
return null;
|
||||
if (charsetName == null) {
|
||||
throw new IllegalArgumentException("charsetName is null");
|
||||
}
|
||||
checkCanonicalName(charsetName);
|
||||
switch (charsetName.toUpperCase()) {
|
||||
case "UTF-8":
|
||||
return new UTF8Charset();
|
||||
default:
|
||||
throw new TUnsupportedCharsetException(charsetName);
|
||||
}
|
||||
}
|
||||
|
||||
public final String name() {
|
||||
|
@ -56,6 +101,8 @@ public abstract class TCharset implements Comparable<TCharset> {
|
|||
return canonicalName;
|
||||
}
|
||||
|
||||
public abstract boolean contains(TCharset cs);
|
||||
|
||||
public abstract TCharsetDecoder newDecoder();
|
||||
|
||||
public abstract TCharsetEncoder newEncoder();
|
||||
|
@ -63,4 +110,35 @@ public abstract class TCharset implements Comparable<TCharset> {
|
|||
public boolean canEncode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public final TCharBuffer decode(TByteBuffer bb) {
|
||||
try {
|
||||
return newDecoder()
|
||||
.onMalformedInput(TCodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(TCodingErrorAction.REPLACE)
|
||||
.decode(bb);
|
||||
} catch (TCharacterCodingException e) {
|
||||
throw new AssertionError("Should never been thrown", e);
|
||||
}
|
||||
}
|
||||
|
||||
public final TByteBuffer encode(TCharBuffer cb) {
|
||||
try {
|
||||
return newEncoder()
|
||||
.onMalformedInput(TCodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(TCodingErrorAction.REPLACE)
|
||||
.encode(cb);
|
||||
} catch (TCharacterCodingException e) {
|
||||
throw new AssertionError("Should never been thrown", e);
|
||||
}
|
||||
}
|
||||
|
||||
public final TByteBuffer encode(String str) {
|
||||
return encode(TCharBuffer.wrap(str));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int compareTo(TCharset that) {
|
||||
return canonicalName.compareToIgnoreCase(that.canonicalName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,230 @@
|
|||
*/
|
||||
package org.teavm.classlib.java.nio.charset;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.teavm.classlib.java.nio.TByteBuffer;
|
||||
import org.teavm.classlib.java.nio.TCharBuffer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class TCharsetDecoder {
|
||||
public abstract class TCharsetDecoder {
|
||||
private static final int INIT = 0;
|
||||
private static final int IN_PROGRESS = 1;
|
||||
private static final int END = 2;
|
||||
private static final int FLUSH = 2;
|
||||
private TCharset charset;
|
||||
private float averageCharsPerByte;
|
||||
private float maxCharsPerByte;
|
||||
private String replacement = "?";
|
||||
private TCodingErrorAction malformedAction = TCodingErrorAction.REPORT;
|
||||
private TCodingErrorAction unmappableAction = TCodingErrorAction.REPORT;
|
||||
private int state;
|
||||
|
||||
protected TCharsetDecoder(TCharset cs, float averageCharsPerByte, float maxCharsPerByte) {
|
||||
if (averageCharsPerByte <= 0) {
|
||||
throw new IllegalArgumentException("averageCharsPerByte must be positive. Actual value is " +
|
||||
averageCharsPerByte);
|
||||
}
|
||||
if (maxCharsPerByte <= 0) {
|
||||
throw new IllegalArgumentException("maxCharsPerByte must be positive. Actual value is " +
|
||||
maxCharsPerByte);
|
||||
}
|
||||
this.charset = cs;
|
||||
this.averageCharsPerByte = averageCharsPerByte;
|
||||
this.maxCharsPerByte = maxCharsPerByte;
|
||||
}
|
||||
|
||||
public final TCharset charset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
public final String replacement() {
|
||||
return replacement;
|
||||
}
|
||||
|
||||
public final TCharsetDecoder replaceWith(String newReplacement) {
|
||||
if (newReplacement == null || newReplacement.isEmpty()) {
|
||||
throw new IllegalArgumentException("New replacement is null or empty");
|
||||
}
|
||||
this.replacement = newReplacement;
|
||||
implReplaceWith(newReplacement);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void implReplaceWith(@SuppressWarnings("unused") String newReplacement) {
|
||||
}
|
||||
|
||||
public TCodingErrorAction malformedInputAction() {
|
||||
return malformedAction;
|
||||
}
|
||||
|
||||
public final TCharsetDecoder onMalformedInput(TCodingErrorAction newAction) {
|
||||
if (newAction == null) {
|
||||
throw new IllegalArgumentException("newAction must be non-null");
|
||||
}
|
||||
this.malformedAction = newAction;
|
||||
implOnMalformedInput(newAction);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void implOnMalformedInput(@SuppressWarnings("unused") TCodingErrorAction newAction) {
|
||||
}
|
||||
|
||||
public TCodingErrorAction unmappableCharacterAction() {
|
||||
return unmappableAction;
|
||||
}
|
||||
|
||||
public final TCharsetDecoder onUnmappableCharacter(TCodingErrorAction newAction) {
|
||||
if (newAction == null) {
|
||||
throw new IllegalArgumentException("newAction must be non-null");
|
||||
}
|
||||
this.unmappableAction = newAction;
|
||||
implOnUnmappableCharacter(newAction);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void implOnUnmappableCharacter(@SuppressWarnings("unused") TCodingErrorAction newAction) {
|
||||
}
|
||||
|
||||
public final float averageCharsPerByte() {
|
||||
return averageCharsPerByte;
|
||||
}
|
||||
|
||||
public final float maxCharsPerByte() {
|
||||
return maxCharsPerByte;
|
||||
}
|
||||
|
||||
public final TCoderResult decode(TByteBuffer in, TCharBuffer out, boolean endOfInput) {
|
||||
if (state == END && !endOfInput || state == FLUSH) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
state = !endOfInput ? IN_PROGRESS : END;
|
||||
while (true) {
|
||||
TCoderResult result;
|
||||
try {
|
||||
result = decodeLoop(in, out);
|
||||
} catch (RuntimeException e) {
|
||||
throw new TCoderMalfunctionError(e);
|
||||
}
|
||||
if (result.isOverflow()) {
|
||||
return result;
|
||||
} else if (result.isUnderflow()) {
|
||||
if (endOfInput) {
|
||||
state = END;
|
||||
return TCoderResult.malformedForLength(in.remaining());
|
||||
}
|
||||
return result;
|
||||
} else if (result.isMalformed()) {
|
||||
if (malformedAction == TCodingErrorAction.REPORT) {
|
||||
return result;
|
||||
}
|
||||
if (malformedAction == TCodingErrorAction.REPLACE) {
|
||||
if (out.remaining() < replacement.length()) {
|
||||
return TCoderResult.OVERFLOW;
|
||||
}
|
||||
out.put(replacement);
|
||||
}
|
||||
in.position(in.position() + result.length());
|
||||
} else if (result.isUnmappable()) {
|
||||
if (unmappableAction == TCodingErrorAction.REPORT) {
|
||||
return result;
|
||||
}
|
||||
if (unmappableAction == TCodingErrorAction.REPLACE) {
|
||||
if (out.remaining() < replacement.length()) {
|
||||
return TCoderResult.OVERFLOW;
|
||||
}
|
||||
out.put(replacement);
|
||||
}
|
||||
in.position(in.position() + result.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final TCoderResult flush(TCharBuffer out) {
|
||||
if (state != FLUSH && state != END) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
state = FLUSH;
|
||||
return implFlush(out);
|
||||
}
|
||||
|
||||
public final TCharsetDecoder reset() {
|
||||
state = INIT;
|
||||
implReset();
|
||||
return this;
|
||||
}
|
||||
|
||||
public final TCharBuffer decode(TByteBuffer in) throws TCharacterCodingException {
|
||||
if (state != INIT && state != FLUSH) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (in.remaining() == 0) {
|
||||
return TCharBuffer.allocate(0);
|
||||
}
|
||||
if (state != INIT) {
|
||||
reset();
|
||||
}
|
||||
|
||||
TCharBuffer out = TCharBuffer.allocate((int)(averageCharsPerByte * in.remaining()));
|
||||
TCoderResult result;
|
||||
while (true) {
|
||||
result = decode(in, out, false);
|
||||
if (result.isUnderflow()) {
|
||||
break;
|
||||
} else if (result.isOverflow()) {
|
||||
out = expand(out);
|
||||
}
|
||||
if (result.isError()) {
|
||||
result.throwException();
|
||||
}
|
||||
}
|
||||
|
||||
result = decode(in, out, true);
|
||||
if (result.isError()) {
|
||||
result.throwException();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
result = flush(out);
|
||||
if (result.isUnderflow()) {
|
||||
break;
|
||||
} else {
|
||||
out = expand(out);
|
||||
}
|
||||
}
|
||||
|
||||
out.flip();
|
||||
return out;
|
||||
}
|
||||
|
||||
public boolean isAutoDetecting() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCharsetDetected() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public TCharset detectedCharset() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private TCharBuffer expand(TCharBuffer buffer) {
|
||||
char[] array = buffer.array();
|
||||
array = Arrays.copyOf(array, array.length * 2);
|
||||
TCharBuffer result = TCharBuffer.wrap(array);
|
||||
result.position(buffer.position());
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract TCoderResult decodeLoop(TByteBuffer in, TCharBuffer out);
|
||||
|
||||
protected TCoderResult implFlush(@SuppressWarnings("unused") TCharBuffer out) {
|
||||
return TCoderResult.UNDERFLOW;
|
||||
}
|
||||
|
||||
protected void implReset() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.classlib.java.nio.charset;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.teavm.classlib.java.nio.TByteBuffer;
|
||||
import org.teavm.classlib.java.nio.TCharBuffer;
|
||||
|
||||
|
@ -23,11 +24,11 @@ import org.teavm.classlib.java.nio.TCharBuffer;
|
|||
* @author Alexey Andreev
|
||||
*/
|
||||
public abstract class TCharsetEncoder {
|
||||
private static final int READY = 0;
|
||||
private static final int READY = 4;
|
||||
private static final int ONGOING = 1;
|
||||
private static final int END = 2;
|
||||
private static final int FLUSH = 3;
|
||||
private static final int INIT = 4;
|
||||
private static final int INIT = 0;
|
||||
private TCharset charset;
|
||||
private byte[] replacement;
|
||||
private float averageBytesPerChar;
|
||||
|
@ -35,7 +36,6 @@ public abstract class TCharsetEncoder {
|
|||
private TCodingErrorAction malformedAction = TCodingErrorAction.REPORT;
|
||||
private TCodingErrorAction unmappableAction = TCodingErrorAction.REPORT;
|
||||
private int status;
|
||||
private boolean finished;
|
||||
|
||||
protected TCharsetEncoder(TCharset cs, float averageBytesPerChar, float maxBytesPerChar, byte[] replacement) {
|
||||
checkReplacement(replacement);
|
||||
|
@ -43,7 +43,6 @@ public abstract class TCharsetEncoder {
|
|||
this.replacement = replacement.clone();
|
||||
this.averageBytesPerChar = averageBytesPerChar;
|
||||
this.maxBytesPerChar = maxBytesPerChar;
|
||||
status = INIT;
|
||||
}
|
||||
|
||||
protected TCharsetEncoder(TCharset cs, float averageBytesPerChar, float maxBytesPerChar) {
|
||||
|
@ -115,25 +114,19 @@ public abstract class TCharsetEncoder {
|
|||
}
|
||||
|
||||
public final TCoderResult encode(TCharBuffer in, TByteBuffer out, boolean endOfInput) {
|
||||
if (status == READY && finished && !endOfInput) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
if (status == FLUSH || !endOfInput && status == END) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
status = endOfInput ? END : ONGOING;
|
||||
TCoderResult result;
|
||||
while (true) {
|
||||
try {
|
||||
result = encodeLoop(in, out);
|
||||
} catch (TBufferOverflowException e) {
|
||||
throw new TCoderMalfunctionError(e);
|
||||
} catch (TBufferUnderflowException e) {
|
||||
} catch (RuntimeException e) {
|
||||
throw new TCoderMalfunctionError(e);
|
||||
}
|
||||
if (result == TCoderResult.UNDERFLOW) {
|
||||
status = endOfInput ? END : ONGOING;
|
||||
if (result.isUnderflow()) {
|
||||
if (endOfInput) {
|
||||
int remaining = in.remaining();
|
||||
if (remaining > 0) {
|
||||
|
@ -144,14 +137,10 @@ public abstract class TCharsetEncoder {
|
|||
} else {
|
||||
return result;
|
||||
}
|
||||
} else if (result == TCoderResult.OVERFLOW) {
|
||||
status = endOfInput ? END : ONGOING;
|
||||
} else if (result.isOverflow()) {
|
||||
return result;
|
||||
}
|
||||
TCodingErrorAction action = malformedAction;
|
||||
if (result.isUnmappable()) {
|
||||
action = unmappableAction;
|
||||
}
|
||||
TCodingErrorAction action = result.isUnmappable() ? unmappableAction : malformedAction;
|
||||
if (action == TCodingErrorAction.REPLACE) {
|
||||
if (out.remaining() < replacement.length) {
|
||||
return TCoderResult.OVERFLOW;
|
||||
|
@ -171,9 +160,9 @@ public abstract class TCharsetEncoder {
|
|||
return TByteBuffer.allocate(0);
|
||||
}
|
||||
reset();
|
||||
int length = (int)(in.remaining() * averageBytesPerChar);
|
||||
TByteBuffer output = TByteBuffer.allocate(length);
|
||||
TCoderResult result = null;
|
||||
TByteBuffer output = TByteBuffer.allocate((int)(in.remaining() * averageBytesPerChar));
|
||||
|
||||
TCoderResult result;
|
||||
while (true) {
|
||||
result = encode(in, output, false);
|
||||
if (result == TCoderResult.UNDERFLOW) {
|
||||
|
@ -182,31 +171,25 @@ public abstract class TCharsetEncoder {
|
|||
output = allocateMore(output);
|
||||
continue;
|
||||
}
|
||||
checkCoderResult(result);
|
||||
if (result.isError()) {
|
||||
result.throwException();
|
||||
}
|
||||
}
|
||||
|
||||
result = encode(in, output, true);
|
||||
checkCoderResult(result);
|
||||
if (result.isError()) {
|
||||
result.throwException();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
result = flush(output);
|
||||
if (result == TCoderResult.UNDERFLOW) {
|
||||
output.flip();
|
||||
if (result.isUnderflow()) {
|
||||
break;
|
||||
} else if (result == TCoderResult.OVERFLOW) {
|
||||
} else if (result.isOverflow()) {
|
||||
output = allocateMore(output);
|
||||
continue;
|
||||
}
|
||||
checkCoderResult(result);
|
||||
output.flip();
|
||||
if (result.isMalformed()) {
|
||||
throw new TMalformedInputException(result.length());
|
||||
} else if (result.isUnmappable()) {
|
||||
throw new TUnmappableCharacterException(result.length());
|
||||
}
|
||||
break;
|
||||
}
|
||||
status = READY;
|
||||
finished = true;
|
||||
output.flip();
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -249,21 +232,11 @@ public abstract class TCharsetEncoder {
|
|||
return implCanEncode(cb);
|
||||
}
|
||||
|
||||
private void checkCoderResult(TCoderResult result) throws TCharacterCodingException {
|
||||
if (malformedAction == TCodingErrorAction.REPORT && result.isMalformed()) {
|
||||
throw new TMalformedInputException(result.length());
|
||||
} else if (unmappableAction == TCodingErrorAction.REPORT && result.isUnmappable()) {
|
||||
throw new TUnmappableCharacterException(result.length());
|
||||
}
|
||||
}
|
||||
|
||||
private TByteBuffer allocateMore(TByteBuffer output) {
|
||||
if (output.capacity() == 0) {
|
||||
return TByteBuffer.allocate(1);
|
||||
}
|
||||
TByteBuffer result = TByteBuffer.allocate(output.capacity() * 2);
|
||||
output.flip();
|
||||
result.put(output);
|
||||
private TByteBuffer allocateMore(TByteBuffer buffer) {
|
||||
byte[] array = buffer.array();
|
||||
array = Arrays.copyOf(array, array.length * 2);
|
||||
TByteBuffer result = TByteBuffer.wrap(array);
|
||||
result.position(buffer.position());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.nio.charset;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class TIllegalCharsetNameException extends IllegalArgumentException {
|
||||
private static final long serialVersionUID = -6848948447254188722L;
|
||||
private String charsetName;
|
||||
|
||||
public TIllegalCharsetNameException(String charsetName) {
|
||||
this.charsetName = charsetName;
|
||||
}
|
||||
|
||||
public String getCharsetName() {
|
||||
return charsetName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.nio.charset;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class TUnsupportedCharsetException extends IllegalArgumentException {
|
||||
private static final long serialVersionUID = 2668607022458967777L;
|
||||
private String charsetName;
|
||||
|
||||
public TUnsupportedCharsetException(String charsetName) {
|
||||
this.charsetName = charsetName;
|
||||
}
|
||||
|
||||
public String getCharsetName() {
|
||||
return charsetName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.nio.charset.impl;
|
||||
|
||||
import org.teavm.classlib.java.nio.charset.TCharset;
|
||||
import org.teavm.classlib.java.nio.charset.TCharsetDecoder;
|
||||
import org.teavm.classlib.java.nio.charset.TCharsetEncoder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class UTF8Charset extends TCharset {
|
||||
public UTF8Charset() {
|
||||
super("UTF-8", new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(TCharset cs) {
|
||||
return cs == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TCharsetDecoder newDecoder() {
|
||||
return new UTF8Decoder(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TCharsetEncoder newEncoder() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.nio.charset.impl;
|
||||
|
||||
import org.teavm.classlib.impl.charset.UTF16Helper;
|
||||
import org.teavm.classlib.java.nio.TByteBuffer;
|
||||
import org.teavm.classlib.java.nio.TCharBuffer;
|
||||
import org.teavm.classlib.java.nio.charset.TCharset;
|
||||
import org.teavm.classlib.java.nio.charset.TCharsetDecoder;
|
||||
import org.teavm.classlib.java.nio.charset.TCoderResult;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class UTF8Decoder extends TCharsetDecoder {
|
||||
public UTF8Decoder(TCharset cs) {
|
||||
super(cs, 1f / 3, 0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TCoderResult decodeLoop(TByteBuffer in, TCharBuffer out) {
|
||||
while (true) {
|
||||
if (in.remaining() < 4) {
|
||||
return TCoderResult.UNDERFLOW;
|
||||
}
|
||||
if (!out.hasRemaining()) {
|
||||
return TCoderResult.OVERFLOW;
|
||||
}
|
||||
int b = in.get() & 0xFF;
|
||||
if ((b & 0x80) == 0) {
|
||||
out.put((char)b);
|
||||
} else if ((b & 0xE0) == 0xC0) {
|
||||
if (!in.hasRemaining()) {
|
||||
in.position(in.position() - 1);
|
||||
return TCoderResult.UNDERFLOW;
|
||||
}
|
||||
out.put((char)(((b & 0x1F) << 6) | (in.get() & 0x3F)));
|
||||
} else if ((b & 0xF0) == 0xE0) {
|
||||
if (in.remaining() < 2) {
|
||||
in.position(in.position() - 1);
|
||||
return TCoderResult.UNDERFLOW;
|
||||
}
|
||||
byte b2 = in.get();
|
||||
byte b3 = in.get();
|
||||
char c = (char)(((b & 0x0F) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3F));
|
||||
if (Character.isSurrogate(c)) {
|
||||
in.position(in.position() - 2);
|
||||
return TCoderResult.malformedForLength(3);
|
||||
}
|
||||
out.put(c);
|
||||
} else if ((b & 0xF8) == 0xF0) {
|
||||
if (in.remaining() < 3) {
|
||||
in.position(in.position() - 1);
|
||||
return TCoderResult.UNDERFLOW;
|
||||
}
|
||||
if (out.remaining() < 3) {
|
||||
in.position(in.position() - 1);
|
||||
return TCoderResult.OVERFLOW;
|
||||
}
|
||||
byte b2 = in.get();
|
||||
byte b3 = in.get();
|
||||
byte b4 = in.get();
|
||||
int code = ((b & 0x07) << 18) | ((b2 & 0x3f) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F);
|
||||
out.put(UTF16Helper.highSurrogate(code));
|
||||
out.put(UTF16Helper.lowSurrogate(code));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user