| /******************************************************************************* |
| * Copyright (c) 2000, 2007 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 |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.ui.console; |
| |
| import java.util.HashMap; |
| |
| import org.eclipse.core.runtime.jobs.ISchedulingRule; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.BadPositionCategoryException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.ui.internal.console.ConsoleDocument; |
| import org.eclipse.ui.internal.console.ConsoleHyperlinkPosition; |
| import org.eclipse.ui.internal.console.ConsolePatternMatcher; |
| import org.eclipse.ui.part.IPageBookViewPage; |
| |
| /** |
| * An abstract text console that supports regular expression matching and |
| * hyperlinks. |
| * <p> |
| * Pattern match listeners can be registered with a console programmatically |
| * or via the <code>org.eclipse.ui.console.consolePatternMatchListeners</code> |
| * extension point. |
| * </p> |
| * <p> |
| * Clients may subclass this class. Subclasses must provide a document partitioner. |
| * </p> |
| * @since 3.1 |
| */ |
| public abstract class TextConsole extends AbstractConsole { |
| |
| /** |
| * The current width of the console. Used for fixed width consoles. |
| * A value of <=0 means does not have a fixed width. |
| */ |
| private int fConsoleWidth; |
| /** |
| * The current tab width |
| */ |
| private int fTabWidth; |
| /** |
| * The font used by this console |
| */ |
| private Font fFont; |
| |
| /** |
| * The background color used by this console or <code>null</code> if default |
| */ |
| private Color fBackground; |
| |
| /** |
| * The Console's regular expression pattern matcher |
| */ |
| private ConsolePatternMatcher fPatternMatcher; |
| |
| /** |
| * The Console's document |
| */ |
| private ConsoleDocument fDocument; |
| |
| /** |
| * indication that the console's partitioner is not expecting more input |
| */ |
| private boolean fPartitionerFinished = false; |
| |
| /** |
| * Indication that the console's pattern matcher has finished. |
| * (all matches have been found and all listeners notified) |
| */ |
| private boolean fMatcherFinished = false; |
| |
| /** |
| * indication that the console output complete property has been fired |
| */ |
| private boolean fCompleteFired = false; |
| |
| |
| /** |
| * Map of client defined attributes |
| */ |
| private HashMap fAttributes = new HashMap(); |
| |
| private IConsoleManager fConsoleManager = ConsolePlugin.getDefault().getConsoleManager(); |
| |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ui.console.AbstractConsole#dispose() |
| */ |
| protected void dispose() { |
| super.dispose(); |
| fFont = null; |
| synchronized(fAttributes) { |
| fAttributes.clear(); |
| } |
| } |
| /** |
| * Constructs a console with the given name, image descriptor, 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 autoLifecycle whether lifecycle methods should be called automatically |
| * when this console is added/removed from the console manager |
| */ |
| public TextConsole(String name, String consoleType, ImageDescriptor imageDescriptor, boolean autoLifecycle) { |
| super(name, consoleType, imageDescriptor, autoLifecycle); |
| fDocument = new ConsoleDocument(); |
| fDocument.addPositionCategory(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY); |
| fPatternMatcher = new ConsolePatternMatcher(this); |
| fDocument.addDocumentListener(fPatternMatcher); |
| fTabWidth = IConsoleConstants.DEFAULT_TAB_SIZE; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ui.console.IConsole#createPage(org.eclipse.ui.console.IConsoleView) |
| */ |
| public IPageBookViewPage createPage(IConsoleView view) { |
| return new TextConsolePage(this, view); |
| } |
| |
| /** |
| * Returns this console's document. |
| * <p> |
| * Note that a console may or may not support direct manipulation of its document. |
| * For example, an I/O console document and its partitions are produced from the |
| * streams connected to it, and clients are not intended to modify the document's |
| * contents. |
| * </p> |
| * |
| * @return this console's document |
| */ |
| public IDocument getDocument() { |
| return fDocument; |
| } |
| |
| /** |
| * Returns the current width of this console. A value of zero of less |
| * indicates this console has no fixed width. |
| * |
| * @return the current width of this console |
| */ |
| public int getConsoleWidth() { |
| return fConsoleWidth; |
| } |
| |
| /** |
| * Sets the width of this console in characters. Any value greater than zero |
| * will cause this console to have a fixed width. |
| * |
| * @param width the width to make this console. Values of 0 or less imply |
| * the console does not have any fixed width. |
| */ |
| public void setConsoleWidth(int width) { |
| if (fConsoleWidth != width) { |
| int old = fConsoleWidth; |
| fConsoleWidth = width; |
| |
| firePropertyChange(this, IConsoleConstants.P_CONSOLE_WIDTH, new Integer(old), new Integer(fConsoleWidth)); |
| } |
| } |
| |
| /** |
| * Sets the tab width used in this console. |
| * |
| * @param newTabWidth the tab width |
| */ |
| public void setTabWidth(final int newTabWidth) { |
| if (fTabWidth != newTabWidth) { |
| final int oldTabWidth = fTabWidth; |
| fTabWidth = newTabWidth; |
| ConsolePlugin.getStandardDisplay().asyncExec(new Runnable() { |
| public void run() { |
| firePropertyChange(TextConsole.this, IConsoleConstants.P_TAB_SIZE, new Integer(oldTabWidth), new Integer(fTabWidth)); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Returns the tab width used in this console. |
| * |
| * @return tab width used in this console |
| */ |
| public int getTabWidth() { |
| return fTabWidth; |
| } |
| |
| /** |
| * Returns the font used by this console. Must be called in the UI thread. |
| * |
| * @return font used by this console |
| */ |
| public Font getFont() { |
| if (fFont == null) { |
| fFont = getDefaultFont(); |
| } |
| return fFont; |
| } |
| |
| /** |
| * Returns the default text font. |
| * |
| * @return the default text font |
| */ |
| private Font getDefaultFont() { |
| return JFaceResources.getFont(JFaceResources.TEXT_FONT); |
| } |
| |
| /** |
| * Sets the font used by this console. Specify <code>null</code> to use |
| * the default text font. |
| * |
| * @param newFont font, or <code>null</code> to indicate the default font |
| */ |
| public void setFont(Font newFont) { |
| // ensure font is initialized |
| getFont(); |
| // translate null to default font |
| if (newFont == null) { |
| newFont = getDefaultFont(); |
| } |
| // fire property change if required |
| if (!fFont.equals(newFont)) { |
| Font old = fFont; |
| fFont = newFont; |
| firePropertyChange(this, IConsoleConstants.P_FONT, old, fFont); |
| } |
| } |
| |
| /** |
| * Sets the background color used by this console. Specify <code>null</code> to use |
| * the default background color. |
| * |
| * @param background background color or <code>null</code> for default |
| * @since 3.3 |
| * @deprecated use setBackground(Color) instead |
| */ |
| public void setBackgrond(Color background) { |
| setBackground(background); |
| } |
| |
| /** |
| * Sets the background color used by this console. Specify <code>null</code> to use |
| * the default background color. |
| * |
| * @param background background color or <code>null</code> for default |
| * @since 3.3 |
| */ |
| public void setBackground(Color background) { |
| if (fBackground == null) { |
| if (background == null) { |
| return; |
| } |
| } else if (fBackground.equals(background)){ |
| return; |
| } |
| Color old = fBackground; |
| fBackground = background; |
| firePropertyChange(this, IConsoleConstants.P_BACKGROUND_COLOR, old, fBackground); |
| } |
| |
| /** |
| * Returns the background color to use for this console or <code>null</code> for the |
| * default background color. |
| * |
| * @return background color or <code>null</code> for default |
| * @since 3.3 |
| */ |
| public Color getBackground() { |
| return fBackground; |
| } |
| |
| /** |
| * Clears the console. |
| * <p> |
| * Since a console may or may not support direct manipulation |
| * of its document's contents, this method should be called to clear a text console's |
| * document. The default implementation sets this console's document content |
| * to the empty string directly. Subclasses should override as required. |
| * </p> |
| */ |
| public void clearConsole() { |
| IDocument document = getDocument(); |
| if (document != null) { |
| document.set(""); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Returns the console's document partitioner. |
| * @return The console's document partitioner |
| */ |
| protected abstract IConsoleDocumentPartitioner getPartitioner(); |
| |
| /** |
| * Returns all hyperlinks in this console. |
| * |
| * @return all hyperlinks in this console |
| */ |
| public IHyperlink[] getHyperlinks() { |
| try { |
| Position[] positions = getDocument().getPositions(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY); |
| IHyperlink[] hyperlinks = new IHyperlink[positions.length]; |
| for (int i = 0; i < positions.length; i++) { |
| ConsoleHyperlinkPosition position = (ConsoleHyperlinkPosition) positions[i]; |
| hyperlinks[i] = position.getHyperLink(); |
| } |
| return hyperlinks; |
| } catch (BadPositionCategoryException e) { |
| return new IHyperlink[0]; |
| } |
| } |
| |
| /** |
| * Returns the hyperlink at the given offset of <code>null</code> if none. |
| * |
| * @param offset offset for which a hyperlink is requested |
| * @return the hyperlink at the given offset of <code>null</code> if none |
| */ |
| public IHyperlink getHyperlink(int offset) { |
| try { |
| IDocument document = getDocument(); |
| if (document != null) { |
| Position[] positions = document.getPositions(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY); |
| Position position = findPosition(offset, positions); |
| if (position instanceof ConsoleHyperlinkPosition) { |
| return ((ConsoleHyperlinkPosition) position).getHyperLink(); |
| } |
| } |
| } catch (BadPositionCategoryException e) { |
| } |
| return null; |
| } |
| |
| /** |
| * Binary search for the position at a given offset. |
| * |
| * @param offset the offset whose position should be found |
| * @return the position containing the offset, or <code>null</code> |
| */ |
| private Position findPosition(int offset, Position[] positions) { |
| |
| if (positions.length == 0) |
| return null; |
| |
| int left= 0; |
| int right= positions.length -1; |
| int mid= 0; |
| Position position= null; |
| |
| while (left < right) { |
| |
| mid= (left + right) / 2; |
| |
| position= positions[mid]; |
| if (offset < position.getOffset()) { |
| if (left == mid) |
| right= left; |
| else |
| right= mid -1; |
| } else if (offset > (position.getOffset() + position.getLength() - 1)) { |
| if (right == mid) |
| left= right; |
| else |
| left= mid +1; |
| } else { |
| left= right= mid; |
| } |
| } |
| |
| position= positions[left]; |
| if (offset >= position.getOffset() && (offset < (position.getOffset() + position.getLength()))) { |
| return position; |
| } |
| return null; |
| } |
| |
| /** |
| * Adds the given pattern match listener to this console. The listener will |
| * be connected and receive match notifications. Has no effect if an identical |
| * listener has already been added. |
| * |
| * @param listener the listener to add |
| */ |
| public void addPatternMatchListener(IPatternMatchListener listener) { |
| fPatternMatcher.addPatternMatchListener(listener); |
| } |
| |
| /** |
| * Removes the given pattern match listener from this console. The listener will be |
| * disconnected and will no longer receive match notifications. Has no effect |
| * if the listener was not previously added. |
| * |
| * @param listener the pattern match listener to remove |
| */ |
| public void removePatternMatchListener(IPatternMatchListener listener) { |
| fPatternMatcher.removePatternMatchListener(listener); |
| } |
| |
| |
| /** |
| * Job scheduling rule that prevent the job from running if the console's PatternMatcher |
| * is active. |
| */ |
| private class MatcherSchedulingRule implements ISchedulingRule { |
| public boolean contains(ISchedulingRule rule) { |
| return rule == this; |
| } |
| |
| public boolean isConflicting(ISchedulingRule rule) { |
| if (contains(rule)) { |
| return true; |
| } |
| if (rule != this && rule instanceof MatcherSchedulingRule) { |
| return (((MatcherSchedulingRule)rule).getConsole() == TextConsole.this); |
| } |
| return false; |
| } |
| |
| public TextConsole getConsole() { |
| return TextConsole.this; |
| } |
| } |
| |
| /** |
| * Returns a scheduling rule which can be used to prevent jobs from running |
| * while this console's pattern matcher is active. |
| * <p> |
| * Although this scheduling rule prevents jobs from running at the same time as |
| * pattern matching jobs for this console, it does not enforce any ordering of jobs. |
| * Since 3.2, pattern matching jobs belong to the job family identified by the console |
| * object that matching is occurring on. To ensure a job runs after all scheduled pattern |
| * matching is complete, clients must join on this console's job family. |
| * </p> |
| * @return a scheduling rule which can be used to prevent jobs from running |
| * while this console's pattern matcher is active |
| */ |
| public ISchedulingRule getSchedulingRule() { |
| return new MatcherSchedulingRule(); |
| } |
| |
| /** |
| * This console's partitioner should call this method when it is not expecting any new data |
| * to be appended to the document. |
| */ |
| public void partitionerFinished() { |
| fPatternMatcher.forceFinalMatching(); |
| fPartitionerFinished = true; |
| checkFinished(); |
| } |
| |
| /** |
| * Called by this console's pattern matcher when matching is complete. |
| * <p> |
| * Clients should not call this method. |
| * <p> |
| */ |
| public void matcherFinished() { |
| fMatcherFinished = true; |
| fDocument.removeDocumentListener(fPatternMatcher); |
| checkFinished(); |
| } |
| |
| /** |
| * Fires the console output complete property change event. |
| */ |
| private synchronized void checkFinished() { |
| if (!fCompleteFired && fPartitionerFinished && fMatcherFinished ) { |
| fCompleteFired = true; |
| firePropertyChange(this, IConsoleConstants.P_CONSOLE_OUTPUT_COMPLETE, null, null); |
| } |
| } |
| |
| /** |
| * Adds a hyperlink to this console. |
| * |
| * @param hyperlink the hyperlink to add |
| * @param offset the offset in the console document at which the hyperlink should be added |
| * @param length the length of the text which should be hyperlinked |
| * @throws BadLocationException if the specified location is not valid. |
| */ |
| public void addHyperlink(IHyperlink hyperlink, int offset, int length) throws BadLocationException { |
| IDocument document = getDocument(); |
| ConsoleHyperlinkPosition hyperlinkPosition = new ConsoleHyperlinkPosition(hyperlink, offset, length); |
| try { |
| document.addPosition(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY, hyperlinkPosition); |
| fConsoleManager.refresh(this); |
| } catch (BadPositionCategoryException e) { |
| ConsolePlugin.log(e); |
| } |
| } |
| |
| /** |
| * Returns the region associated with the given hyperlink. |
| * |
| * @param link hyperlink |
| * @return the region associated with the hyperlink or null if the hyperlink is not found. |
| */ |
| public IRegion getRegion(IHyperlink link) { |
| try { |
| IDocument doc = getDocument(); |
| if (doc != null) { |
| Position[] positions = doc.getPositions(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY); |
| for (int i = 0; i < positions.length; i++) { |
| ConsoleHyperlinkPosition position = (ConsoleHyperlinkPosition)positions[i]; |
| if (position.getHyperLink().equals(link)) { |
| return new Region(position.getOffset(), position.getLength()); |
| } |
| } |
| } |
| } catch (BadPositionCategoryException e) { |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the attribute associated with the specified key. |
| * |
| * @param key attribute key |
| * @return the attribute associated with the specified key |
| */ |
| public Object getAttribute(String key) { |
| synchronized (fAttributes) { |
| return fAttributes.get(key); |
| } |
| } |
| |
| /** |
| * Sets an attribute value. Intended for client data. |
| * |
| * @param key attribute key |
| * @param value attribute value |
| */ |
| public void setAttribute(String key, Object value) { |
| synchronized(fAttributes) { |
| fAttributes.put(key, value); |
| } |
| } |
| } |