diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TBufferOverflowException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TBufferOverflowException.java new file mode 100644 index 000000000..d690455bf --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TBufferOverflowException.java @@ -0,0 +1,30 @@ +/* + * 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; + +import org.teavm.classlib.java.lang.TRuntimeException; + +/** + * + * @author Alexey Andreev + */ +public class TBufferOverflowException extends TRuntimeException { + private static final long serialVersionUID = -94154470269902198L; + + public TBufferOverflowException() { + super(); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TBufferUnderflowException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TBufferUnderflowException.java new file mode 100644 index 000000000..e628337e8 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TBufferUnderflowException.java @@ -0,0 +1,28 @@ +/* + * 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 TBufferUnderflowException extends RuntimeException { + private static final long serialVersionUID = 1162643612868888150L; + + public TBufferUnderflowException() { + super(); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharacterCodingException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharacterCodingException.java new file mode 100644 index 000000000..0597dbda9 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharacterCodingException.java @@ -0,0 +1,30 @@ +/* + * 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; + +import java.io.IOException; + +/** + * + * @author Alexey Andreev + */ +public class TCharacterCodingException extends IOException { + private static final long serialVersionUID = -2432441010404243409L; + + public TCharacterCodingException() { + super(); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharset.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharset.java new file mode 100644 index 000000000..26bc0c1ef --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharset.java @@ -0,0 +1,66 @@ +/* + * 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; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author Alexey Andreev + */ +public abstract class TCharset implements Comparable { + private String canonicalName; + private String[] aliases; + private Set aliasSet; + + protected TCharset(String canonicalName, String[] aliases) { + this.canonicalName = canonicalName; + this.aliases = aliases.clone(); + } + + public static TCharset forName(String charsetName) { + return null; + } + + public final String name() { + return canonicalName; + } + + public final Set aliases() { + if (aliasSet == null) { + aliasSet = new HashSet<>(); + for (String alias : aliases) { + aliasSet.add(alias); + } + aliasSet = Collections.unmodifiableSet(aliasSet); + } + return aliasSet; + } + + public String displayName() { + return canonicalName; + } + + public abstract TCharsetDecoder newDecoder(); + + public abstract TCharsetEncoder newEncoder(); + + public boolean canEncode() { + return true; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharsetDecoder.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharsetDecoder.java new file mode 100644 index 000000000..e3587a204 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharsetDecoder.java @@ -0,0 +1,24 @@ +/* + * 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 TCharsetDecoder { + +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharsetEncoder.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharsetEncoder.java new file mode 100644 index 000000000..f09f10d8f --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCharsetEncoder.java @@ -0,0 +1,293 @@ +/* + * 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; + +import org.teavm.classlib.java.nio.TByteBuffer; +import org.teavm.classlib.java.nio.TCharBuffer; + +/** + * + * @author Alexey Andreev + */ +public abstract class TCharsetEncoder { + private static final int READY = 0; + 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 TCharset charset; + private byte[] replacement; + private float averageBytesPerChar; + private float maxBytesPerChar; + 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); + this.charset = cs; + this.replacement = replacement.clone(); + this.averageBytesPerChar = averageBytesPerChar; + this.maxBytesPerChar = maxBytesPerChar; + status = INIT; + } + + protected TCharsetEncoder(TCharset cs, float averageBytesPerChar, float maxBytesPerChar) { + this(cs, averageBytesPerChar, maxBytesPerChar, new byte[] { (byte)'?' }); + } + + public final TCharset charset() { + return charset; + } + + public final byte[] replacement() { + return replacement.clone(); + } + + public final TCharsetEncoder replaceWith(byte[] newReplacement) { + checkReplacement(newReplacement); + this.replacement = newReplacement.clone(); + implReplaceWith(newReplacement); + return this; + } + + private void checkReplacement(byte[] replacement) { + if (replacement == null || replacement.length == 0 || replacement.length < maxBytesPerChar) { + throw new IllegalArgumentException("Replacement preconditions do not hold"); + } + } + + protected void implReplaceWith(@SuppressWarnings("unused") byte[] newReplacement) { + } + + public TCodingErrorAction malformedInputAction() { + return malformedAction; + } + + public final TCharsetEncoder onMalformedInput(TCodingErrorAction newAction) { + if (newAction == null) { + throw new IllegalArgumentException("Action must be non-null"); + } + malformedAction = newAction; + implOnMalformedInput(newAction); + return this; + } + + protected void implOnMalformedInput(@SuppressWarnings("unused") TCodingErrorAction newAction) { + } + + public TCodingErrorAction unmappableCharacterAction() { + return unmappableAction; + } + + public final TCharsetEncoder onUnmappableCharacter(TCodingErrorAction newAction) { + if (newAction == null) { + throw new IllegalArgumentException("Action must be non-null"); + } + unmappableAction = newAction; + implOnUnmappableCharacter(newAction); + return this; + } + + protected void implOnUnmappableCharacter(@SuppressWarnings("unused") TCodingErrorAction newAction) { + } + + public final float averageBytesPerChar() { + return averageBytesPerChar; + } + + public final float maxBytesPerChar() { + return maxBytesPerChar; + } + + 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(); + } + + TCoderResult result; + while (true) { + try { + result = encodeLoop(in, out); + } catch (TBufferOverflowException e) { + throw new TCoderMalfunctionError(e); + } catch (TBufferUnderflowException e) { + throw new TCoderMalfunctionError(e); + } + if (result == TCoderResult.UNDERFLOW) { + status = endOfInput ? END : ONGOING; + if (endOfInput) { + int remaining = in.remaining(); + if (remaining > 0) { + result = TCoderResult.malformedForLength(remaining); + } else { + return result; + } + } else { + return result; + } + } else if (result == TCoderResult.OVERFLOW) { + status = endOfInput ? END : ONGOING; + return result; + } + TCodingErrorAction action = malformedAction; + if (result.isUnmappable()) { + action = unmappableAction; + } + if (action == TCodingErrorAction.REPLACE) { + if (out.remaining() < replacement.length) { + return TCoderResult.OVERFLOW; + } + out.put(replacement); + } else { + if (action != TCodingErrorAction.IGNORE) { + return result; + } + } + in.position(in.position() + result.length()); + } + } + + public final TByteBuffer encode(TCharBuffer in) throws TCharacterCodingException { + if (in.remaining() == 0) { + return TByteBuffer.allocate(0); + } + reset(); + int length = (int)(in.remaining() * averageBytesPerChar); + TByteBuffer output = TByteBuffer.allocate(length); + TCoderResult result = null; + while (true) { + result = encode(in, output, false); + if (result == TCoderResult.UNDERFLOW) { + break; + } else if (result == TCoderResult.OVERFLOW) { + output = allocateMore(output); + continue; + } + checkCoderResult(result); + } + result = encode(in, output, true); + checkCoderResult(result); + + while (true) { + result = flush(output); + if (result == TCoderResult.UNDERFLOW) { + output.flip(); + break; + } else if (result == TCoderResult.OVERFLOW) { + 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; + return output; + } + + protected abstract TCoderResult encodeLoop(TCharBuffer in, TByteBuffer out); + + public boolean canEncode(char c) { + return implCanEncode(TCharBuffer.wrap(new char[] { c })); + } + + private boolean implCanEncode(TCharBuffer cb) { + if (status == FLUSH || status == INIT) { + status = READY; + } + if (status != READY) { + throw new IllegalStateException(); + } + TCodingErrorAction malformBak = malformedAction; + TCodingErrorAction unmapBak = unmappableAction; + onMalformedInput(TCodingErrorAction.REPORT); + onUnmappableCharacter(TCodingErrorAction.REPORT); + boolean result = true; + try { + encode(cb); + } catch (TCharacterCodingException e) { + result = false; + } + onMalformedInput(malformBak); + onUnmappableCharacter(unmapBak); + reset(); + return result; + } + + public boolean canEncode(CharSequence sequence) { + TCharBuffer cb; + if (sequence instanceof TCharBuffer) { + cb = ((TCharBuffer)sequence).duplicate(); + } else { + cb = TCharBuffer.wrap(sequence); + } + 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); + return result; + } + + public final TCoderResult flush(TByteBuffer out) { + if (status != END && status != READY) { + throw new IllegalStateException(); + } + TCoderResult result = implFlush(out); + if (result == TCoderResult.UNDERFLOW) { + status = FLUSH; + } + return result; + } + + protected TCoderResult implFlush(@SuppressWarnings("unused") TByteBuffer out) { + return TCoderResult.UNDERFLOW; + } + + public final TCharsetEncoder reset() { + status = INIT; + implReset(); + return this; + } + + protected void implReset() { + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCoderMalfunctionError.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCoderMalfunctionError.java new file mode 100644 index 000000000..31c2873b3 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCoderMalfunctionError.java @@ -0,0 +1,28 @@ +/* + * 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 TCoderMalfunctionError extends Error { + private static final long serialVersionUID = 8030591981317653860L; + + public TCoderMalfunctionError(Throwable cause) { + super(cause); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCoderResult.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCoderResult.java new file mode 100644 index 000000000..5bedfd3d3 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCoderResult.java @@ -0,0 +1,81 @@ +/* + * 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 TCoderResult { + public static final TCoderResult UNDERFLOW = new TCoderResult((byte)0, 0); + public static final TCoderResult OVERFLOW = new TCoderResult((byte)1, 0); + private byte kind; + private int length; + + TCoderResult(byte kind, int length) { + super(); + this.kind = kind; + this.length = length; + } + + public boolean isUnderflow() { + return kind == 0; + } + + public boolean isOverflow() { + return kind == 1; + } + + public boolean isError() { + return isMalformed() || isUnmappable(); + } + + public boolean isMalformed() { + return kind == 2; + } + + public boolean isUnmappable() { + return kind == 3; + } + + public int length() { + if (!isError()) { + throw new UnsupportedOperationException(); + } + return length; + } + + public static TCoderResult malformedForLength(int length) { + return new TCoderResult((byte)2, length); + } + + public static TCoderResult unmappableForLength(int length) { + return new TCoderResult((byte)3, length); + } + + public void throwException() throws TCharacterCodingException { + switch (kind) { + case 0: + throw new TBufferUnderflowException(); + case 1: + throw new TBufferOverflowException(); + case 2: + throw new TMalformedInputException(length); + case 3: + throw new TUnmappableCharacterException(length); + } + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCodingErrorAction.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCodingErrorAction.java new file mode 100644 index 000000000..2c0633f4a --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TCodingErrorAction.java @@ -0,0 +1,36 @@ +/* + * 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 TCodingErrorAction { + public static final TCodingErrorAction IGNORE = new TCodingErrorAction("IGNORE"); + public static final TCodingErrorAction REPLACE = new TCodingErrorAction("REPLACE"); + public static final TCodingErrorAction REPORT = new TCodingErrorAction("REPORT"); + private String name; + + public TCodingErrorAction(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TMalformedInputException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TMalformedInputException.java new file mode 100644 index 000000000..b3dbd8f79 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TMalformedInputException.java @@ -0,0 +1,38 @@ +/* + * 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 TMalformedInputException extends TCharacterCodingException { + private static final long serialVersionUID = -7677315548693161814L; + private int length; + + public TMalformedInputException(int length) { + this.length = length; + } + + public int getLength() { + return length; + } + + @Override + public String getMessage() { + return "Malformed input of length " + length; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TUnmappableCharacterException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TUnmappableCharacterException.java new file mode 100644 index 000000000..dcf5c1ee5 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/nio/charset/TUnmappableCharacterException.java @@ -0,0 +1,38 @@ +/* + * 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 TUnmappableCharacterException extends TCharacterCodingException { + private static final long serialVersionUID = 4225770757749997199L; + private int length; + + public TUnmappableCharacterException(int length) { + this.length = length; + } + + public int getLength() { + return length; + } + + @Override + public String getMessage() { + return "Unmappable characters of length " + length; + } +}