diff options
10 files changed, 374 insertions, 90 deletions
diff --git a/org.eclipse.debug.tests/META-INF/MANIFEST.MF b/org.eclipse.debug.tests/META-INF/MANIFEST.MF index 5f2db82d6..81028df52 100644 --- a/org.eclipse.debug.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.debug.tests/META-INF/MANIFEST.MF @@ -14,7 +14,8 @@ Require-Bundle: org.eclipse.ui;bundle-version="[3.6.0,4.0.0)", org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)", org.eclipse.debug.core;bundle-version="[3.9.0,4.0.0)", org.eclipse.ui.externaltools;bundle-version="[3.3.0,4.0.0)", - org.eclipse.ui.console;bundle-version="[3.7.0,4.0.0)" + org.eclipse.ui.console;bundle-version="[3.7.0,4.0.0)", + org.eclipse.text Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-Vendor: %providerName diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java index 2204d97f1..5c3e1f70c 100644 --- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corporation and others. + * Copyright (c) 2009, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,11 +12,9 @@ *******************************************************************************/ package org.eclipse.debug.tests; -import junit.framework.Test; -import junit.framework.TestSuite; - import org.eclipse.debug.tests.breakpoint.BreakpointOrderingTests; import org.eclipse.debug.tests.console.ConsoleManagerTests; +import org.eclipse.debug.tests.console.ConsoleTests; import org.eclipse.debug.tests.launching.AcceleratorSubstitutionTests; import org.eclipse.debug.tests.launching.ArgumentParsingTests; import org.eclipse.debug.tests.launching.LaunchConfigurationTests; @@ -41,6 +39,9 @@ import org.eclipse.debug.tests.viewer.model.VirtualViewerSelectionTests; import org.eclipse.debug.tests.viewer.model.VirtualViewerStateTests; import org.eclipse.debug.tests.viewer.model.VirtualViewerUpdateTests; +import junit.framework.Test; +import junit.framework.TestSuite; + /** * Tests for integration and nightly builds. * @@ -104,6 +105,7 @@ public class AutomatedSuite extends TestSuite { // Console view addTest(new TestSuite(ConsoleManagerTests.class)); + addTest(new TestSuite(ConsoleTests.class)); // Launch Groups addTest(new TestSuite(LaunchGroupTests.class)); diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleManagerTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleManagerTests.java index 0e3d7fd4d..fb0a025ec 100644 --- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleManagerTests.java +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleManagerTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 Andrey Loskutov and others. + * Copyright (c) 2016, 2017 Andrey Loskutov and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -16,7 +16,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.swt.SWT; @@ -61,7 +60,7 @@ public class ConsoleManagerTests extends TestCase { manager = ConsolePlugin.getDefault().getConsoleManager(); IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); hideWelcomePage(activePage); - processUIEvents(100); + TestHelper.processUIEvents(100); consoles = new ConsoleMock[count]; for (int i = 0; i < count; i++) { final ConsoleMock console = new ConsoleMock(i + 1); @@ -72,7 +71,7 @@ public class ConsoleManagerTests extends TestCase { IViewPart consoleView = activePage.showView("org.eclipse.ui.console.ConsoleView"); //$NON-NLS-1$ activePage.activate(consoleView); - processUIEvents(100); + TestHelper.processUIEvents(100); // The test is unstable ("show" event on the the first console seem to // be not always sent), so make sure console view has shown at least @@ -80,8 +79,8 @@ public class ConsoleManagerTests extends TestCase { firstConsole = new ConsoleMock(0); manager.addConsoles(new ConsoleMock[] { firstConsole }); manager.showConsoleView(firstConsole); - waitForJobs(); - processUIEvents(100); + TestHelper.waitForJobs(); + TestHelper.processUIEvents(100); ConsoleMock.allShownConsoles.set(0); } @@ -90,7 +89,7 @@ public class ConsoleManagerTests extends TestCase { executorService.shutdownNow(); manager.removeConsoles(consoles); manager.removeConsoles(new ConsoleMock[] { firstConsole }); - processUIEvents(100); + TestHelper.processUIEvents(100); super.tearDown(); } @@ -104,7 +103,7 @@ public class ConsoleManagerTests extends TestCase { } if (intro != null) { activePage.hideView(intro); - processUIEvents(100); + TestHelper.processUIEvents(100); } } @@ -124,15 +123,15 @@ public class ConsoleManagerTests extends TestCase { showConsole(console); } System.out.println("All tasks scheduled, processing UI events now..."); //$NON-NLS-1$ - processUIEvents(1000); + TestHelper.processUIEvents(1000); // Console manager starts a job with delay, let wait for him a bit System.out.println("Waiting on jobs now..."); //$NON-NLS-1$ - waitForJobs(); + TestHelper.waitForJobs(); // Give UI a chance to proceed pending console manager jobs System.out.println("Done with jobs, processing UI events again..."); //$NON-NLS-1$ - processUIEvents(3000); + TestHelper.processUIEvents(3000); executorService.shutdown(); @@ -140,7 +139,7 @@ public class ConsoleManagerTests extends TestCase { boolean OK = waitForExecutorService(); if (!OK) { System.out.println("Timed out..."); //$NON-NLS-1$ - processUIEvents(10000); + TestHelper.processUIEvents(10000); // timeout? assertTrue("Timeout occurred while waiting on console to be shown", //$NON-NLS-1$ @@ -157,35 +156,11 @@ public class ConsoleManagerTests extends TestCase { if (executorService.awaitTermination(1, TimeUnit.SECONDS)) { return true; } - processUIEvents(100); + TestHelper.processUIEvents(100); } return false; } - private void processUIEvents(final long millis) { - long start = System.currentTimeMillis(); - while (System.currentTimeMillis() - start < millis) { - while (PlatformUI.getWorkbench().getDisplay().readAndDispatch()) { - // loop untile the queue is empty - } - } - } - - private void waitForJobs() throws InterruptedException { - if (Display.getCurrent() == null) { - Thread.sleep(200); - } else { - processUIEvents(200); - } - while (!Job.getJobManager().isIdle()) { - if (Display.getCurrent() == null) { - Thread.sleep(200); - } else { - processUIEvents(200); - } - } - } - private void showConsole(final ConsoleMock console) { executorService.execute(new Runnable() { @Override @@ -197,7 +172,7 @@ public class ConsoleManagerTests extends TestCase { latch.await(1, TimeUnit.MINUTES); System.out.println("Requesting to show: " + console); //$NON-NLS-1$ manager.showConsoleView(console); - waitForJobs(); + TestHelper.waitForJobs(); } catch (InterruptedException e) { e.printStackTrace(); Thread.interrupted(); @@ -277,4 +252,5 @@ public class ConsoleManagerTests extends TestCase { return "mock #" + number; //$NON-NLS-1$ } } + } diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleTests.java new file mode 100644 index 000000000..3939c4d3a --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleTests.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2017 Andreas Loth and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andreas Loth - initial API and implementation + *******************************************************************************/ + +package org.eclipse.debug.tests.console; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.ui.console.IConsoleConstants; +import org.eclipse.ui.console.IOConsoleOutputStream; +import org.eclipse.ui.console.MessageConsole; + +import junit.framework.TestCase; + + +public class ConsoleTests extends TestCase { + + public ConsoleTests() { + super("ConsoleTests"); //$NON-NLS-1$ + } + + public ConsoleTests(String name) { + super(name); + } + + public void testConsoleOutputStreamEncoding() throws IOException, InterruptedException { + String testString = "abc\u00e4\u00f6\u00fcdef"; //$NON-NLS-1$ + // abcdef need 1 byte in UTF-8 each + // äöü (\u00e4\u00f6\u00fc) need 2 bytes each + byte[] testStringBuffer = testString.getBytes(StandardCharsets.UTF_8); + TestCase.assertEquals("Test string \"" + testString + "\" should consist of 12 UTF-8 bytes", 12, testStringBuffer.length); //$NON-NLS-1$ //$NON-NLS-2$ + MessageConsole console = new MessageConsole("Test Console", //$NON-NLS-1$ + IConsoleConstants.MESSAGE_CONSOLE_TYPE, null, StandardCharsets.UTF_8.name(), true); + IDocument document = console.getDocument(); + TestHelper.waitForJobs(); + TestCase.assertEquals("Document should be empty", "", document.get()); //$NON-NLS-1$ //$NON-NLS-2$ + try (IOConsoleOutputStream outStream = console.newOutputStream()) { + outStream.write(testStringBuffer, 0, 6); + // half of ö (\u00f6) is written so we don't expect this char in + // output but all previous chars can be decoded + TestHelper.waitForJobs(); + TestCase.assertEquals("First 4 chars should be written", testString.substring(0, 4), document.get()); //$NON-NLS-1$ + outStream.write(testStringBuffer, 6, 6); + // all remaining bytes are written so we expect the whole string + // including the ö (\u00f6) which was at buffer boundary + TestHelper.waitForJobs(); + TestCase.assertEquals("whole test string should be written", testString, document.get()); //$NON-NLS-1$ + } + } + +} diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/TestHelper.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/TestHelper.java new file mode 100644 index 000000000..981459a79 --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/TestHelper.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2017 Andreas Loth and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andreas Loth - initial API and implementation + *******************************************************************************/ + +package org.eclipse.debug.tests.console; + +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.PlatformUI; + + +public final class TestHelper { + + private TestHelper() { + throw new AssertionError("No instances of this utility class!"); //$NON-NLS-1$ + } + + public static void processUIEvents(final long millis) { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < millis) { + while (PlatformUI.getWorkbench().getDisplay().readAndDispatch()) { + // loop untile the queue is empty + } + } + } + + public static void waitForJobs() throws InterruptedException { + if (Display.getCurrent() == null) { + Thread.sleep(200); + } else { + processUIEvents(200); + } + while (!Job.getJobManager().isIdle()) { + if (Display.getCurrent() == null) { + Thread.sleep(200); + } else { + processUIEvents(200); + } + } + } + +} diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java b/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java index 1f573f70f..44cbfc4d7 100644 --- a/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java +++ b/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -15,12 +15,12 @@ package org.eclipse.ui.console; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.jface.resource.ImageDescriptor; - import org.eclipse.ui.WorkbenchEncoding; import org.eclipse.ui.internal.console.IOConsolePage; import org.eclipse.ui.internal.console.IOConsolePartitioner; @@ -54,7 +54,7 @@ public class IOConsole extends TextConsole { /** * The encoding used to for displaying console output. */ - private String fEncoding = WorkbenchEncoding.getWorkbenchDefaultEncoding(); + private Charset charset; /** @@ -68,7 +68,7 @@ public class IOConsole extends TextConsole { * when this console is added/removed from the console manager */ public IOConsole(String name, String consoleType, ImageDescriptor imageDescriptor, boolean autoLifecycle) { - this(name, consoleType, imageDescriptor, null, autoLifecycle); + this(name, consoleType, imageDescriptor, (String)null, autoLifecycle); } /** @@ -83,10 +83,31 @@ public class IOConsole extends TextConsole { * when this console is added/removed from the console manager */ public IOConsole(String name, String consoleType, ImageDescriptor imageDescriptor, String encoding, boolean autoLifecycle) { + this(name, consoleType, imageDescriptor, + encoding == null + ? Charset.forName(WorkbenchEncoding.getWorkbenchDefaultEncoding()) + : Charset.forName(encoding), + autoLifecycle); + } + + /** + * Constructs a console with the given name, type, image, encoding and + * lifecycle. + * + * @param name name to display for this console + * @param consoleType console type identifier or <code>null</code> + * @param imageDescriptor image to display for this console or + * <code>null</code> + * @param charset the encoding that should be used to render the text, must + * not be <code>null</code> + * @param autoLifecycle whether lifecycle methods should be called + * automatically when this console is added/removed from the + * console manager + * @since 3.7 + */ + public IOConsole(String name, String consoleType, ImageDescriptor imageDescriptor, Charset charset, boolean autoLifecycle) { super(name, consoleType, imageDescriptor, autoLifecycle); - if (encoding != null) { - fEncoding = encoding; - } + this.charset = charset; synchronized (openStreams) { inputStream = new IOConsoleInputStream(this); openStreams.add(inputStream); @@ -144,8 +165,7 @@ public class IOConsole extends TextConsole { * @return a new output stream connected to this console */ public IOConsoleOutputStream newOutputStream() { - IOConsoleOutputStream outputStream = new IOConsoleOutputStream(this); - outputStream.setEncoding(fEncoding); + IOConsoleOutputStream outputStream = new IOConsoleOutputStream(this, this.charset); synchronized(openStreams) { openStreams.add(outputStream); } @@ -301,6 +321,19 @@ public class IOConsole extends TextConsole { * @since 3.3 */ public String getEncoding() { - return fEncoding; + return this.charset.name(); } + + /** + * Returns the Charset for this console, or <code>null</code> to indicate + * default encoding. + * + * @return the Charset for this console, or <code>null</code> to indicate + * default encoding + * @since 3.7 + */ + public Charset getCharset() { + return this.charset; + } + } diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsoleOutputStream.java b/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsoleOutputStream.java index 08efd3bfd..7e78ddf6a 100644 --- a/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsoleOutputStream.java +++ b/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsoleOutputStream.java @@ -13,10 +13,11 @@ package org.eclipse.ui.console; import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.Charset; import org.eclipse.swt.graphics.Color; -import org.eclipse.ui.WorkbenchEncoding; import org.eclipse.ui.internal.console.IOConsolePartitioner; +import org.eclipse.ui.internal.console.StreamDecoder; /** * OutputStream used to write to an IOConsole. @@ -66,10 +67,7 @@ public class IOConsoleOutputStream extends OutputStream { */ private int fontStyle; - private String fEncoding; - private String fDefaultEncoding = WorkbenchEncoding.getWorkbenchDefaultEncoding(); - - private boolean fNeedsEncoding = false; + private StreamDecoder decoder; private boolean prependCR; @@ -78,8 +76,9 @@ public class IOConsoleOutputStream extends OutputStream { * * @param console I/O console */ - IOConsoleOutputStream(IOConsole console) { - this.console = console; + IOConsoleOutputStream(IOConsole console, Charset charset) { + this.decoder = new StreamDecoder(charset); + this.console = console; this.partitioner = (IOConsolePartitioner) console.getPartitioner(); } @@ -195,11 +194,9 @@ public class IOConsoleOutputStream extends OutputStream { */ @Override public void write(byte[] b, int off, int len) throws IOException { - if (fNeedsEncoding) { - encodedWrite(new String(b, off, len, fEncoding)); - } else { - encodedWrite(new String(b, off, len)); - } + StringBuilder builder = new StringBuilder(); + this.decoder.decode(builder, b, off, len); + encodedWrite(builder.toString()); } /* * (non-Javadoc) @@ -218,12 +215,51 @@ public class IOConsoleOutputStream extends OutputStream { write(new byte[] {(byte)b}, 0, 1); } + /** + * Writes a character array to the attached console. + * + * @param buffer the char array to write to the attached console + * @throws IOException if the stream is closed + * @since 3.7 + */ + public void write(char[] buffer) throws IOException { + String str = new String(buffer); + this.encodedWrite(str); + } + + /** + * Writes a character array using specified offset and length to the + * attached console. + * + * @param buffer the char array to write to the attached console. + * @param off the initial offset + * @param len the length + * @throws IOException if the stream is closed + * @since 3.7 + */ + public void write(char[] buffer, int off, int len) throws IOException { + String str = new String(buffer, off, len); + this.encodedWrite(str); + } + /** - * Writes a string to the attached console. - * - * @param str the string to write to the attached console. - * @throws IOException if the stream is closed. - */ + * Writes a character sequence to the attached console. + * + * @param chars the string/characters to write to the attached console. + * @throws IOException if the stream is closed. + * @since 3.7 + */ + public void write(CharSequence chars) throws IOException { + String str = chars.toString(); + encodedWrite(str); + } + + /** + * Writes a string to the attached console. + * + * @param str the string to write to the attached console + * @throws IOException if the stream is closed + */ public void write(String str) throws IOException { encodedWrite(str); } @@ -267,7 +303,25 @@ public class IOConsoleOutputStream extends OutputStream { * @param encoding encoding identifier */ public void setEncoding(String encoding) { - fEncoding = encoding; - fNeedsEncoding = (fEncoding!=null) && (!fEncoding.equals(fDefaultEncoding)); + Charset charset = Charset.forName(encoding); + try { + this.setCharset(charset); + } catch (IOException ioe) { + // ignore exception while writing final characters + // to avoid API break + } + } + + /** + * @param charset set the Charset for the attached console + * @throws IOException if the stream is closed + * @since 3.7 + */ + public void setCharset(Charset charset) throws IOException { + StringBuilder builder = new StringBuilder(); + this.decoder.finish(builder); + this.encodedWrite(builder.toString()); + this.decoder = new StreamDecoder(charset); } + } diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/console/MessageConsole.java b/org.eclipse.ui.console/src/org/eclipse/ui/console/MessageConsole.java index d7ea0d0f2..aa399164e 100644 --- a/org.eclipse.ui.console/src/org/eclipse/ui/console/MessageConsole.java +++ b/org.eclipse.ui.console/src/org/eclipse/ui/console/MessageConsole.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2013 IBM Corporation and others. + * Copyright (c) 2000, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -124,7 +124,7 @@ public class MessageConsole extends IOConsole { * @return a new message stream connected to this console */ public MessageConsoleStream newMessageStream() { - return new MessageConsoleStream(this); + return new MessageConsoleStream(this, this.getCharset()); } /* (non-Javadoc) diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/console/MessageConsoleStream.java b/org.eclipse.ui.console/src/org/eclipse/ui/console/MessageConsoleStream.java index 274e8a8f2..c8593d53a 100644 --- a/org.eclipse.ui.console/src/org/eclipse/ui/console/MessageConsoleStream.java +++ b/org.eclipse.ui.console/src/org/eclipse/ui/console/MessageConsoleStream.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 20017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,6 +11,9 @@ package org.eclipse.ui.console; import java.io.IOException; +import java.nio.charset.Charset; + +import org.eclipse.ui.WorkbenchEncoding; /** * Used to write messages to a message console. A message console may have more @@ -33,22 +36,32 @@ import java.io.IOException; * @noextend This class is not intended to be subclassed by clients. */ public class MessageConsoleStream extends IOConsoleOutputStream { - + private MessageConsole fMessageConsole; - + /** * Constructs a new stream connected to the given console. - * + * * @param console the console to write messages to */ public MessageConsoleStream(MessageConsole console) { - super(console); + this(console, Charset.forName(WorkbenchEncoding.getWorkbenchDefaultEncoding())); + } + + /** + * Constructs a new stream connected to the given console. + * + * @param console the console to write messages to + * @since 3.7 + */ + public MessageConsoleStream(MessageConsole console, Charset charset) { + super(console, charset); fMessageConsole = console; } - + /** * Appends the specified message to this stream. - * + * * @param message message to append */ public void print(String message) { @@ -58,8 +71,8 @@ public class MessageConsoleStream extends IOConsoleOutputStream { ConsolePlugin.log(e); } } - - + + /** * Appends a line separator string to this stream. */ @@ -69,24 +82,24 @@ public class MessageConsoleStream extends IOConsoleOutputStream { } catch (IOException e) { ConsolePlugin.log(e); } - } - + } + /** * Appends the specified message to this stream, followed by a line * separator string. - * + * * @param message message to print */ public void println(String message) { print(message + "\n"); //$NON-NLS-1$ - } - + } + /** * Returns the console this stream is connected to. - * + * * @return the console this stream is connected to */ public MessageConsole getConsole() { return fMessageConsole; - } + } } diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/StreamDecoder.java b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/StreamDecoder.java new file mode 100644 index 000000000..dde14fdcd --- /dev/null +++ b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/StreamDecoder.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2017 Andreas Loth and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andreas Loth - initial API and implementation + *******************************************************************************/ + +package org.eclipse.ui.internal.console; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + + +/** + * @since 3.7 + */ +public class StreamDecoder { + + static private final int BUFFER_SIZE = 4096; + + private final CharsetDecoder decoder; + private final ByteBuffer inputBuffer; + private final CharBuffer outputBuffer; + + public StreamDecoder(Charset charset) { + this.decoder = charset.newDecoder(); + this.decoder.onMalformedInput(CodingErrorAction.REPLACE); + this.decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + this.inputBuffer = ByteBuffer.allocate(StreamDecoder.BUFFER_SIZE); + this.inputBuffer.flip(); + this.outputBuffer = CharBuffer.allocate(StreamDecoder.BUFFER_SIZE); + } + + private void consume(StringBuilder consumer) { + this.outputBuffer.flip(); + consumer.append(this.outputBuffer); + this.outputBuffer.clear(); + } + + private void internalDecode(StringBuilder consumer, byte[] buffer, int offset, int length, boolean last) { + assert (offset >= 0); + assert (length >= 0); + int position = offset; + int end = offset + length; + assert (end <= buffer.length); + boolean finished = false; + do { + CoderResult result = this.decoder.decode(this.inputBuffer, this.outputBuffer, last); + if (result.isOverflow()) { + this.consume(consumer); + } else if (result.isUnderflow()) { + this.inputBuffer.compact(); + int remaining = this.inputBuffer.remaining(); + assert (remaining > 0); + int read = Math.min(remaining, end - position); + if (read > 0) { + this.inputBuffer.put(buffer, position, read); + position += read; + } else { + finished = true; + } + this.inputBuffer.flip(); + } else { + assert false; + } + } while (!finished); + } + + public void decode(StringBuilder consumer, byte[] buffer, int offset, int length) { + this.internalDecode(consumer, buffer, offset, length, false); + this.consume(consumer); + } + + public void finish(StringBuilder consumer) { + this.internalDecode(consumer, new byte[0], 0, 0, true); + CoderResult result; + do { + result = this.decoder.flush(this.outputBuffer); + if (result.isOverflow()) { + this.consume(consumer); + } else { + assert result.isUnderflow(); + } + } while (!result.isUnderflow()); + this.consume(consumer); + } + +} |