/***************************************************************** * 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.cayenne.modeler.dialog; import org.apache.cayenne.modeler.Application; import org.apache.cayenne.modeler.pref.ComponentGeometry; import org.apache.cayenne.modeler.util.CayenneController; import org.apache.cayenne.util.Util; import javax.swing.text.*; import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.PrintWriter; import java.io.StringWriter; import java.text.DateFormat; import java.util.Date; /** * Implementation for modeler log console functionality */ public class LogConsole extends CayenneController { /** * How much characters are allowed in console */ private static final int TEXT_MAX_LENGTH = 500000; /** * Property to store user preference */ public static final String SHOW_CONSOLE_PROPERTY = "show.log.console"; /** * Property to store 'is-docked' preference */ public static final String DOCKED_PROPERTY = "log.console.docked"; /** * Message date format */ private static final DateFormat FORMAT = DateFormat.getDateTimeInstance(); /** * Red color style for severe messages */ public static final MutableAttributeSet ERROR_STYLE; /** * Red bold color style for fatal messages */ public static final MutableAttributeSet FATAL_STYLE; /** * Dark red color style for warn messages */ public static final MutableAttributeSet WARN_STYLE; /** * Blue color style for info messages */ public static final MutableAttributeSet INFO_STYLE; /** * Style for debug messages */ public static final MutableAttributeSet DEBUG_STYLE; static { ERROR_STYLE = new SimpleAttributeSet(); StyleConstants.setForeground(ERROR_STYLE, Color.RED); FATAL_STYLE = new SimpleAttributeSet(); StyleConstants.setForeground(FATAL_STYLE, Color.RED); StyleConstants.setBold(FATAL_STYLE, true); WARN_STYLE = new SimpleAttributeSet(); StyleConstants.setForeground(WARN_STYLE, Color.RED.darker()); INFO_STYLE = new SimpleAttributeSet(); StyleConstants.setForeground(INFO_STYLE, new Color(32, 65, 150)); DEBUG_STYLE = new SimpleAttributeSet(); StyleConstants.setForeground(DEBUG_STYLE, Color.GRAY); } /** * Lone log console instance */ private static LogConsole instance; /** * Swing console window */ private LogConsoleView view; /** * Window, which contains the console. Might be non-visible, if the console is docked */ private LogConsoleWindow logWindow; /** * Whether auto-scrolling should be enabled for the console text area. * Currently not included in UI */ private boolean autoScroll; /** * Flag, indicating that no more logging to Swing component is appreciated. * This is useful to prevent logging messages when they are no more needed, e.g. on * JVM shutdown */ private boolean loggingStopped; public LogConsole() { super((CayenneController) null); view = new LogConsoleView(); autoScroll = true; initBindings(); } protected void initBindings() { view.getClearItem().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { clear(); } }); view.getCopyItem().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { copy(); } }); view.getDockItem().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // Log console should be visible disappear(); setConsoleProperty(DOCKED_PROPERTY, !getConsoleProperty(DOCKED_PROPERTY)); appear(); } }); } /*public void showMenu(){ view.menu }*/ /** * Clears the console */ public void clear() { view.getLogView().setText(""); } /** * Shows the console, in separate window or in main frame */ private void appear() { if (!getConsoleProperty(DOCKED_PROPERTY)) { view.setDocked(false); if (logWindow == null) { logWindow = new LogConsoleWindow(this); ComponentGeometry geometry = new ComponentGeometry(getClass(), null); geometry.bind(logWindow, 600, 300, 0); } logWindow.setContentPane(view); logWindow.validate(); logWindow.setVisible(true); } else { view.setDocked(true); Application.getFrame().setDockComponent(view); } } /** * Hides the console */ private void disappear() { if (!getConsoleProperty(DOCKED_PROPERTY)) { logWindow.dispose(); logWindow = null; } else { Application.getFrame().setDockComponent(null); } } /** * Copies selected text from the console to system buffer */ public void copy() { String selectedText = view.getLogView().getSelectedText(); // If nothing is selected, we copy the whole text if (Util.isEmptyString(selectedText)) { Document doc = view.getLogView().getDocument(); try { selectedText = doc.getText(0, doc.getLength()); } catch (BadLocationException e) { return; } } Clipboard sysClip = Toolkit.getDefaultToolkit().getSystemClipboard(); StringSelection data = new StringSelection(selectedText); sysClip.setContents(data, data); } /** * Shows or hides the console window */ public void toggle() { boolean needShow = !getConsoleProperty(SHOW_CONSOLE_PROPERTY); setConsoleProperty(SHOW_CONSOLE_PROPERTY, needShow); if (needShow) { appear(); } else { disappear(); } } /** * Shows the console if the preference 'SHOW_CONSOLE_PROPERTY' is set to true */ public void showConsoleIfNeeded() { if (getConsoleProperty(SHOW_CONSOLE_PROPERTY)) { appear(); } } /** * Sets the property, depending on last user's choice */ public void setConsoleProperty(String prop, boolean b) { Application.getInstance().getPreferencesNode(getClass(), null).putBoolean(prop, b); } /** * @return a boolean property */ public boolean getConsoleProperty(String prop) { return Application.getInstance().getPreferencesNode(getClass(), null).getBoolean(prop, false); } /** * Appends a message to the console. * @param style Message font, color etc. */ public void appendMessage(String level, String message, AttributeSet style) { appendMessage(level, message, null, style); } /** * Appends a message and (or) an exception * @param style Message font, color etc. */ public void appendMessage(String level, String message, Throwable ex, AttributeSet style) { if (loggingStopped) { return; } Document doc = view.getLogView().getDocument(); //truncate if needed if (doc.getLength() > TEXT_MAX_LENGTH) { clear(); } StringBuilder newText = new StringBuilder(FORMAT.format(new Date())) //.append(System.getProperty("line.separator")) .append(" ").append(level.toUpperCase()).append(": "); if (message != null) { // Append the message newText.append(message).append(System.getProperty("line.separator")); } if (ex != null) { // Append the stack trace StringWriter out = new StringWriter(); PrintWriter printer = new PrintWriter(out); ex.printStackTrace(printer); printer.flush(); newText.append(out.toString()).append(System.getProperty("line.separator")); } try { doc.insertString(doc.getLength(), newText.toString(), style); if (autoScroll) { view.getLogView().setCaretPosition(doc.getLength() - 1); } } catch (BadLocationException ignored) { //should not happen } } @Override public Component getView() { return view; } /** * Stop logging and don't print any more messages to text area */ public void stopLogging() { if(!getConsoleProperty(DOCKED_PROPERTY)){ setConsoleProperty(SHOW_CONSOLE_PROPERTY, false); } loggingStopped = true; } /** * @return lone log console instance */ public static LogConsole getInstance() { if (instance == null) { instance = new LogConsole(); } return instance; } }