Author: rwhitcomb Date: Mon Jun 4 15:41:02 2018 New Revision: 1832859 URL: http://svn.apache.org/viewvc?rev=1832859&view=rev Log: Improvements to "Console" debugging package: * Allow override of output streams (defaults to System.out and .err). * Make Console into a non-static class, but with a default that always logs to the system console. * Make a TextAreaOutputStream that can make itself into a PrintStream for use with Console. * Make a test program (TextAreaConsoleTest) that uses a small text area to demonstrate logging using the Console class inside a window.
Added: pivot/trunk/tests/src/org/apache/pivot/tests/TextAreaConsoleTest.java pivot/trunk/tests/src/org/apache/pivot/tests/console_test.bxml pivot/trunk/wtk/src/org/apache/pivot/wtk/util/TextAreaOutputStream.java Modified: pivot/trunk/core/src/org/apache/pivot/util/Console.java Modified: pivot/trunk/core/src/org/apache/pivot/util/Console.java URL: http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/util/Console.java?rev=1832859&r1=1832858&r2=1832859&view=diff ============================================================================== --- pivot/trunk/core/src/org/apache/pivot/util/Console.java (original) +++ pivot/trunk/core/src/org/apache/pivot/util/Console.java Mon Jun 4 15:41:02 2018 @@ -16,34 +16,123 @@ */ package org.apache.pivot.util; +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.net.UnknownHostException; +import java.nio.charset.CharacterCodingException; +import java.nio.file.NoSuchFileException; + /** * Utility class for a simple log to the console, for example from scripts. */ public final class Console { - private Console() { + /** Used to output a null message value, so user knows a null "something" was logged. */ + private static final String NULL = "<null>"; + + /** For static logging, the singleton instance pointing to the system console to use. */ + private static final Console CONSOLE = new Console(); + + /** The print stream to use for "output" logging. Defaults to {@link System#out}. */ + private PrintStream out = System.out; + /** The print stream to use for "error" logging. Defaults to {@link System#err}. */ + private PrintStream err = System.err; + + + public Console() { + } + + public Console(final PrintStream stream) { + setStreams(stream); + } + + public Console(final PrintStream output, final PrintStream error) { + setOutputStream(output); + setErrorStream(error); + } + + public static Console getDefault() { + return CONSOLE; + } + + private void checkNotDefault() { + if (this == CONSOLE) { + throw new IllegalStateException("Cannot modify default Console object."); + } + } + + public void setStreams(final PrintStream stream) { + checkNotDefault(); + out = err = stream; + } + + public void setOutputStream(final PrintStream output) { + checkNotDefault(); + out = output; + } + + public void setErrorStream(final PrintStream error) { + checkNotDefault(); + err = error; } - public static final void log(String message) { + /** + * Log the given message to the "output" stream. + * + * @param message The message to log. + * @see #logOutput + */ + public void log(final String message) { logOutput(message); } - public static final void log(Throwable t) { + /** + * Log the given exception (basically print the stack trace) to the "error" stream. + * + * @param t The exception to log. + */ + public void log(final Throwable t) { if (t != null) { - t.printStackTrace(); + t.printStackTrace(err); } } - public static final void logExceptionMessage(Throwable t) { - logOutput(t.getMessage()); + /** + * Get a user-friendly message from the given exception. + * + * @param t The throwable (exception) in question. + * @return Start with the localized exception message, but use the + * simple name of the exception if there is no message, or if the + * exception is one of a short list of "funny" exceptions where the + * message by itself is ambiguous, prepend the simple name of the + * exception to the message. + */ + public static String getExceptionMessage(final Throwable t) { + String msg = t.getLocalizedMessage(); + if (msg == null || msg.isEmpty()) { + msg = t.getClass().getSimpleName(); + } else if ((t instanceof UnknownHostException) + || (t instanceof NoClassDefFoundError) + || (t instanceof ClassNotFoundException) + || (t instanceof NullPointerException) + || (t instanceof CharacterCodingException) + || (t instanceof FileNotFoundException) + || (t instanceof NoSuchFileException)) { + msg = String.format("%1$s: %2$s", t.getClass().getSimpleName(), msg); + } + return msg; } - public static final void logOutput(String message) { - System.out.println(message != null ? message : ""); + public void logExceptionMessage(final Throwable t) { + logOutput(getExceptionMessage(t)); } - public static final void logError(String message) { - System.err.println(message != null ? message : ""); + public void logOutput(final String message) { + out.println(message == null ? NULL : message); + } + + public void logError(final String message) { + err.println(message == null ? NULL : message); } /** @@ -53,9 +142,12 @@ public final class Console { * or a format string using the remaining args (can be {@code null}). * @param args The optional arguments used to format the final message. */ - public static final void logMethod(String message, Object... args) { - logOutput(ClassUtils.getCallingMethod(1) + ": " + - (message == null ? "" : String.format(message, args))); + public void logMethod(final String message, final Object... args) { + logOutput( + ClassUtils.getCallingMethod(1) + + ": " + + (message == null ? NULL : String.format(message, args)) + ); } /** @@ -67,12 +159,12 @@ public final class Console { * or a format string using the remaining args (can be {@code null}). * @param args The optional arguments used to format the final message. */ - public static final void logMethod(String prefix, String message, Object... args) { + public void logMethod(final String prefix, final String message, final Object... args) { logOutput( - (prefix == null ? "" : prefix + " ") + - ClassUtils.getCallingMethod(1) + - ": " + - (message == null ? "" : String.format(message, args)) + (prefix == null ? "" : prefix + " ") + + ClassUtils.getCallingMethod(1) + + ": " + + (message == null ? NULL : String.format(message, args)) ); } Added: pivot/trunk/tests/src/org/apache/pivot/tests/TextAreaConsoleTest.java URL: http://svn.apache.org/viewvc/pivot/trunk/tests/src/org/apache/pivot/tests/TextAreaConsoleTest.java?rev=1832859&view=auto ============================================================================== --- pivot/trunk/tests/src/org/apache/pivot/tests/TextAreaConsoleTest.java (added) +++ pivot/trunk/tests/src/org/apache/pivot/tests/TextAreaConsoleTest.java Mon Jun 4 15:41:02 2018 @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you 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.apache.pivot.tests; + +import java.io.IOException; +import org.apache.pivot.beans.BXML; +import org.apache.pivot.beans.BXMLSerializer; +import org.apache.pivot.collections.Map; +import org.apache.pivot.util.Console; +import org.apache.pivot.serialization.SerializationException; +import org.apache.pivot.wtk.Application; +import org.apache.pivot.wtk.DesktopApplicationContext; +import org.apache.pivot.wtk.Display; +import org.apache.pivot.wtk.PushButton; +import org.apache.pivot.wtk.TextArea; +import org.apache.pivot.wtk.Window; +import org.apache.pivot.wtk.util.TextAreaOutputStream; + + +public class TextAreaConsoleTest implements Application { + @BXML private Window window; + @BXML private PushButton logMessageButton; + @BXML private TextArea consoleArea; + private Console console; + private int line = 1; + + @Override + public void startup(Display display, Map<String, String> properties) { + BXMLSerializer serializer = new BXMLSerializer(); + try { + serializer.readObject(TextAreaConsoleTest.class, "console_test.bxml"); + serializer.bind(this); + } catch (IOException | SerializationException ex) { + throw new RuntimeException(ex); + } + console = new Console(new TextAreaOutputStream(consoleArea).toPrintStream()); + logMessageButton.getButtonPressListeners().add((button) -> console.log(String.format("%1$d. Hello, World!", line++))); + window.open(display); + } + + @Override + public boolean shutdown(boolean optional) { + if (window != null) { + window.close(); + } + return false; + } + + public static void main(String[] args) { + DesktopApplicationContext.main(TextAreaConsoleTest.class, args); + } +} Added: pivot/trunk/tests/src/org/apache/pivot/tests/console_test.bxml URL: http://svn.apache.org/viewvc/pivot/trunk/tests/src/org/apache/pivot/tests/console_test.bxml?rev=1832859&view=auto ============================================================================== --- pivot/trunk/tests/src/org/apache/pivot/tests/console_test.bxml (added) +++ pivot/trunk/tests/src/org/apache/pivot/tests/console_test.bxml Mon Jun 4 15:41:02 2018 @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to you 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. +--> + +<Window bxml:id="window" title="Console Text Area Test" maximized="true" + xmlns:bxml="http://pivot.apache.org/bxml" + xmlns:collections="org.apache.pivot.collections" + xmlns:content="org.apache.pivot.wtk.content" + xmlns="org.apache.pivot.wtk"> + <TablePane> + <columns> + <TablePane.Column width="1*"/> + </columns> + <rows> + <TablePane.Row height="3*"> + <FlowPane> + <PushButton bxml:id="logMessageButton" buttonData="Log Message"/> + </FlowPane> + </TablePane.Row> + <TablePane.Row height="1*"> + <Border title="Console"> + <ScrollPane> + <TextArea bxml:id="consoleArea" editable="false"/> + </ScrollPane> + </Border> + </TablePane.Row> + </rows> + </TablePane> +</Window> Added: pivot/trunk/wtk/src/org/apache/pivot/wtk/util/TextAreaOutputStream.java URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/util/TextAreaOutputStream.java?rev=1832859&view=auto ============================================================================== --- pivot/trunk/wtk/src/org/apache/pivot/wtk/util/TextAreaOutputStream.java (added) +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/util/TextAreaOutputStream.java Mon Jun 4 15:41:02 2018 @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you 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.apache.pivot.wtk.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import org.apache.pivot.wtk.ApplicationContext; +import org.apache.pivot.wtk.Bounds; +import org.apache.pivot.wtk.TextArea; + +/** + * Creates an {@link OutputStream} that outputs to a {@link TextArea} + * (in the EDT thread, using callbacks) for display. + * <p> Can be used with the {@link org.apache.pivot.util.Console} class for output (using the + * {@link #toPrintStream} method). + */ +public final class TextAreaOutputStream extends OutputStream { + /** The TextArea we are going to stream to. */ + private TextArea textArea; + + /** The buffered line for this stream. */ + private ByteArrayOutputStream lineBuffer = new ByteArrayOutputStream(256); + + /** + * Only constructor given the {@link TextArea} to stream to. + * + * @param textAreaToUse The TextArea to use for output. + */ + public TextAreaOutputStream(final TextArea textAreaToUse) { + this.textArea = textAreaToUse; + } + + /** + * @throws IOException if this stream is already closed. + */ + private void checkIfOpen() throws IOException { + if (textArea == null || lineBuffer == null) { + throw new IOException("TextAreaOutputStream is closed."); + } + } + + /** + * Flush the (byte) line buffer if there is anything cached. + * @param addNewLine If there is anything to flush, also add a newline + * ('\n') character at the end. + */ + private void flushLineBuffer(final boolean addNewLine) { + if (lineBuffer.size() > 0) { + byte[] bytes = lineBuffer.toByteArray(); + // TODO: should we have a charset to use here?? + String text = new String(bytes); + int length = textArea.getCharacterCount(); + textArea.insertText(text, length); + if (addNewLine) { + int newLength = length + text.length(); + textArea.insertText("\n", newLength); + } + lineBuffer.reset(); + Bounds beginningOfLineBounds = textArea.getCharacterBounds(length); + ApplicationContext.queueCallback(() -> textArea.scrollAreaToVisible(beginningOfLineBounds)); + } + } + + @Override + public void close() throws IOException { + flush(); + this.textArea = null; + this.lineBuffer = null; + } + + @Override + public void flush() throws IOException { + checkIfOpen(); + flushLineBuffer(false); + } + + @Override + public void write(final int b) throws IOException { + if (b == '\n') { + flushLineBuffer(true); + } else if (b != '\r') { + lineBuffer.write(b); + } + } + + /** + * @return A new {@link PrintStream} using this object as the basis. + */ + public PrintStream toPrintStream() { + return new PrintStream(this); + } + +}