classlib: add PrintStream.format

This commit is contained in:
Alexey Andreev 2024-03-21 19:14:45 +01:00
parent b792f48b55
commit 403da28830
7 changed files with 143 additions and 12 deletions

View File

@ -17,17 +17,18 @@
package org.teavm.classlib.java.io; package org.teavm.classlib.java.io;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
public class TBufferedOutputStream extends TFilterOutputStream { public class TBufferedOutputStream extends TFilterOutputStream {
protected byte[] buf; protected byte[] buf;
protected int count; protected int count;
public TBufferedOutputStream(TOutputStream out) { public TBufferedOutputStream(OutputStream out) {
super(out); super(out);
buf = new byte[8192]; buf = new byte[8192];
} }
public TBufferedOutputStream(TOutputStream out, int size) { public TBufferedOutputStream(OutputStream out, int size) {
super(out); super(out);
if (size <= 0) { if (size <= 0) {
throw new IllegalArgumentException("size must be > 0"); throw new IllegalArgumentException("size must be > 0");

View File

@ -18,6 +18,7 @@ package org.teavm.classlib.java.io;
import java.io.IOException; import java.io.IOException;
import org.teavm.classlib.java.lang.TMath; import org.teavm.classlib.java.lang.TMath;
import org.teavm.classlib.java.lang.TString; import org.teavm.classlib.java.lang.TString;
import org.teavm.classlib.java.nio.charset.TCharset;
import org.teavm.classlib.java.util.TArrays; import org.teavm.classlib.java.util.TArrays;
public class TByteArrayOutputStream extends TOutputStream { public class TByteArrayOutputStream extends TOutputStream {
@ -66,6 +67,10 @@ public class TByteArrayOutputStream extends TOutputStream {
return new TString(buf, 0, count, charsetName); return new TString(buf, 0, count, charsetName);
} }
public TString toString(TCharset charset) {
return new TString(buf, 0, count, charset);
}
public void writeTo(TOutputStream out) throws IOException { public void writeTo(TOutputStream out) throws IOException {
out.write(buf, 0, count); out.write(buf, 0, count);
} }

View File

@ -14,8 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
package org.teavm.classlib.java.io; package org.teavm.classlib.java.io;
import java.io.IOException; import java.io.IOException;
import org.teavm.classlib.java.lang.*; import java.io.OutputStream;
import org.teavm.classlib.java.lang.TDouble;
import org.teavm.classlib.java.lang.TFloat;
import org.teavm.classlib.java.lang.TInteger;
import org.teavm.classlib.java.lang.TNullPointerException;
import org.teavm.classlib.java.lang.TString;
public class TDataOutputStream extends TFilterOutputStream implements TDataOutput { public class TDataOutputStream extends TFilterOutputStream implements TDataOutput {
/** /**
@ -24,7 +30,7 @@ public class TDataOutputStream extends TFilterOutputStream implements TDataOutpu
protected int written; protected int written;
byte[] buff; byte[] buff;
public TDataOutputStream(TOutputStream out) { public TDataOutputStream(OutputStream out) {
super(out); super(out);
buff = new byte[8]; buff = new byte[8];
} }

View File

@ -16,11 +16,12 @@
package org.teavm.classlib.java.io; package org.teavm.classlib.java.io;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
public class TFilterOutputStream extends TOutputStream { public class TFilterOutputStream extends TOutputStream {
protected TOutputStream out; protected OutputStream out;
public TFilterOutputStream(TOutputStream out) { public TFilterOutputStream(OutputStream out) {
this.out = out; this.out = out;
} }

View File

@ -16,6 +16,8 @@
package org.teavm.classlib.java.io; package org.teavm.classlib.java.io;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.util.Locale;
import org.teavm.classlib.java.lang.TMath; import org.teavm.classlib.java.lang.TMath;
import org.teavm.classlib.java.lang.TObject; import org.teavm.classlib.java.lang.TObject;
import org.teavm.classlib.java.lang.TStringBuilder; import org.teavm.classlib.java.lang.TStringBuilder;
@ -27,6 +29,7 @@ import org.teavm.classlib.java.nio.charset.TCodingErrorAction;
import org.teavm.classlib.java.nio.charset.TIllegalCharsetNameException; import org.teavm.classlib.java.nio.charset.TIllegalCharsetNameException;
import org.teavm.classlib.java.nio.charset.TUnsupportedCharsetException; import org.teavm.classlib.java.nio.charset.TUnsupportedCharsetException;
import org.teavm.classlib.java.nio.charset.impl.TUTF8Charset; import org.teavm.classlib.java.nio.charset.impl.TUTF8Charset;
import org.teavm.classlib.java.util.TFormatter;
public class TPrintStream extends TFilterOutputStream { public class TPrintStream extends TFilterOutputStream {
private boolean autoFlush; private boolean autoFlush;
@ -35,7 +38,7 @@ public class TPrintStream extends TFilterOutputStream {
private char[] buffer = new char[32]; private char[] buffer = new char[32];
private TCharset charset; private TCharset charset;
public TPrintStream(TOutputStream out, boolean autoFlush, String encoding) throws TUnsupportedEncodingException { public TPrintStream(OutputStream out, boolean autoFlush, String encoding) throws TUnsupportedEncodingException {
super(out); super(out);
this.autoFlush = autoFlush; this.autoFlush = autoFlush;
try { try {
@ -45,13 +48,23 @@ public class TPrintStream extends TFilterOutputStream {
} }
} }
public TPrintStream(TOutputStream out, boolean autoFlush) { public TPrintStream(OutputStream out, boolean autoFlush) {
super(out); super(out);
this.autoFlush = autoFlush; this.autoFlush = autoFlush;
this.charset = TUTF8Charset.INSTANCE; this.charset = TUTF8Charset.INSTANCE;
} }
public TPrintStream(TOutputStream out) { public TPrintStream(OutputStream out, boolean autoFlush, TCharset charset) {
super(out);
this.autoFlush = autoFlush;
this.charset = charset;
}
public TPrintStream(OutputStream out, TCharset charset) {
this(out, false, charset);
}
public TPrintStream(OutputStream out) {
this(out, false); this(out, false);
} }
@ -157,6 +170,31 @@ public class TPrintStream extends TFilterOutputStream {
} }
} }
private void print(CharSequence s, int begin, int end) {
TCharBuffer src = TCharBuffer.wrap(s, begin, end - begin);
byte[] destBytes = new byte[TMath.max(16, TMath.min(end - begin, 1024))];
TByteBuffer dest = TByteBuffer.wrap(destBytes);
TCharsetEncoder encoder = charset.newEncoder()
.onMalformedInput(TCodingErrorAction.REPLACE)
.onUnmappableCharacter(TCodingErrorAction.REPLACE);
while (true) {
boolean overflow = encoder.encode(src, dest, true).isOverflow();
write(destBytes, 0, dest.position());
dest.clear();
if (!overflow) {
break;
}
}
while (true) {
boolean overflow = encoder.flush(dest).isOverflow();
write(destBytes, 0, dest.position());
dest.clear();
if (!overflow) {
break;
}
}
}
public void print(char c) { public void print(char c) {
buffer[0] = c; buffer[0] = c;
print(buffer, 0, 1); print(buffer, 0, 1);
@ -231,10 +269,54 @@ public class TPrintStream extends TFilterOutputStream {
print('\n'); print('\n');
} }
public TPrintStream format(String format, Object... args) {
return format(Locale.getDefault(), format, args);
}
public TPrintStream format(Locale locale, String format, Object... args) {
if (args == null) {
args = new Object[1];
}
try (var formatter = new TFormatter(getAppendable(), locale)) {
formatter.format(format, args);
if (formatter.ioException() != null) {
errorState = true;
}
}
return this;
}
private void printSB() { private void printSB() {
char[] buffer = sb.length() > this.buffer.length ? new char[sb.length()] : this.buffer; char[] buffer = sb.length() > this.buffer.length ? new char[sb.length()] : this.buffer;
sb.getChars(0, sb.length(), buffer, 0); sb.getChars(0, sb.length(), buffer, 0);
print(buffer, 0, sb.length()); print(buffer, 0, sb.length());
sb.setLength(0); sb.setLength(0);
} }
private Appendable appendable;
private Appendable getAppendable() {
if (appendable == null) {
appendable = new Appendable() {
@Override
public Appendable append(CharSequence csq) {
print(csq, 0, csq.length());
return this;
}
@Override
public Appendable append(CharSequence csq, int start, int end) {
print(csq, start, end);
return this;
}
@Override
public Appendable append(char c) {
print(c);
return this;
}
};
}
return appendable;
}
} }

View File

@ -30,7 +30,6 @@ import org.teavm.classlib.impl.console.StderrOutputStream;
import org.teavm.classlib.impl.console.StdoutOutputStream; import org.teavm.classlib.impl.console.StdoutOutputStream;
import org.teavm.classlib.java.io.TConsole; import org.teavm.classlib.java.io.TConsole;
import org.teavm.classlib.java.io.TInputStream; import org.teavm.classlib.java.io.TInputStream;
import org.teavm.classlib.java.io.TOutputStream;
import org.teavm.classlib.java.io.TPrintStream; import org.teavm.classlib.java.io.TPrintStream;
import org.teavm.classlib.java.lang.reflect.TArray; import org.teavm.classlib.java.lang.reflect.TArray;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
@ -60,7 +59,7 @@ public final class TSystem extends TObject {
if (PlatformDetector.isJavaScript()) { if (PlatformDetector.isJavaScript()) {
outCache = (TPrintStream) (Object) new JSStdoutPrintStream(); outCache = (TPrintStream) (Object) new JSStdoutPrintStream();
} else { } else {
outCache = new TPrintStream((TOutputStream) (Object) StdoutOutputStream.INSTANCE, false); outCache = new TPrintStream(StdoutOutputStream.INSTANCE, false);
} }
} }
return outCache; return outCache;
@ -71,7 +70,7 @@ public final class TSystem extends TObject {
if (PlatformDetector.isJavaScript()) { if (PlatformDetector.isJavaScript()) {
errCache = (TPrintStream) (Object) new JSStderrPrintStream(); errCache = (TPrintStream) (Object) new JSStderrPrintStream();
} else { } else {
errCache = new TPrintStream((TOutputStream) (Object) StderrOutputStream.INSTANCE, false); errCache = new TPrintStream(StderrOutputStream.INSTANCE, false);
} }
} }
return errCache; return errCache;

View File

@ -0,0 +1,37 @@
/*
* Copyright 2024 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.io;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
import org.testng.annotations.Test;
@RunWith(TeaVMTestRunner.class)
public class PrintStreamTest {
@Test
public void format() {
var bytes = new ByteArrayOutputStream();
var stream = new PrintStream(bytes, false, StandardCharsets.UTF_8);
stream.format("n=%d; ", 23);
stream.format("s=%s", null);
stream.flush();
assertEquals("n=23; s=null", bytes.toString(StandardCharsets.UTF_8));
}
}