/*******************************************************************************
 * Copyright (c) 2000, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.texteditor;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;

import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;

import org.eclipse.jface.text.CursorLinePainter;
import org.eclipse.jface.text.IPainter;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.ITextViewerExtension4;
import org.eclipse.jface.text.MarginPainter;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationPainter;
import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy;
import org.eclipse.jface.text.source.AnnotationPainter.ITextStyleStrategy;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.ICharacterPairMatcher;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension5;
import org.eclipse.jface.text.source.MatchingCharacterPainter;




/**
 * Support class used by text editors to draw and update decorations on the
 * source viewer and its rulers. An instance of this class is independent of a
 * certain editor and must be configured with the needed preference keys and
 * helper objects before it can be used.
 * <p>
 * Once configured, an instance may be installed (see
 * {@link #install(IPreferenceStore) install}) on a preference store, from then
 * on monitoring the configured preference settings and changing the respective
 * decorations. Calling {@link #uninstall() uninstall} will unregister the
 * listeners with the preferences store and must be called before changing the
 * preference store by another call to <code>install</code>.<br>
 * {@link #dispose() dispose} will uninstall the support and remove any
 * decorations from the viewer. It is okay to reuse a
 * <code>SourceViewerDecorationSupport</code> instance after disposing it.
 * </p>
 * <p>
 * <code>SourceViewerDecorationSupport</code> can draw the following
 * decorations:
 * </p>
 * <ul>
 * <li>matching character highlighting,</li>
 * <li>current line highlighting,</li>
 * <li>print margin, and</li>
 * <li>annotations.</li>
 * </ul>
 * <p>
 * Annotations are managed for the overview ruler and also drawn onto the text
 * widget by an {@link org.eclipse.jface.text.source.AnnotationPainter
 * AnnotationPainter} instance.
 * </p>
 * <p>
 * Subclasses may add decorations but should adhere to the lifecyle described
 * above.
 * </p>
 *
 * @see org.eclipse.jface.text.source.AnnotationPainter
 * @since 2.1
 */
public class SourceViewerDecorationSupport {


	/**
	 * Draws an iBeam at the given offset, the length is ignored.
	 *
	 * @since 3.0
	 */
	private static final class IBeamStrategy implements IDrawingStrategy {

		@Override
		public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {
			if (gc != null) {

				Point left= textWidget.getLocationAtOffset(offset);
				int x1= left.x;
				int y1= left.y;

				gc.setForeground(color);
				gc.drawLine(x1, y1, x1, left.y + textWidget.getLineHeight(offset) - 1);

			} else {
				/*
				 * The length for IBeam's is always 0, which causes no redraw to occur in
				 * StyledText#redraw(int, int, boolean). We try to normally redraw at length of one,
				 * and up to the line start of the next line if offset is at the end of line. If at
				 * the end of the document, we redraw the entire document as the offset is behind
				 * any content.
				 */
				final int contentLength= textWidget.getCharCount();
				if (offset >= contentLength) {
					textWidget.redraw();
					return;
				}

				char ch= textWidget.getTextRange(offset, 1).charAt(0);
				if (ch == '\r' || ch == '\n') {
					// at the end of a line, redraw up to the next line start
					int nextLine= textWidget.getLineAtOffset(offset) + 1;
					if (nextLine >= textWidget.getLineCount()) {
						/*
						 * Panic code: should not happen, as offset is not the last offset,
						 * and there is a delimiter character at offset.
						 */
						textWidget.redraw();
						return;
					}

					int nextLineOffset= textWidget.getOffsetAtLine(nextLine);
					length= nextLineOffset - offset;
				} else {
					length= 1;
				}

				textWidget.redrawRange(offset, length, true);
			}
		}
	}


	/**
	 * The box drawing strategy.
	 * @since 3.0
	 */
	private static ITextStyleStrategy fgBoxStrategy= new AnnotationPainter.BoxStrategy(SWT.BORDER_SOLID);

	/**
	 * The dashed box drawing strategy.
	 * @since 3.3
	 */
	private static ITextStyleStrategy fgDashedBoxStrategy= new AnnotationPainter.BoxStrategy(SWT.BORDER_DASH);

	/**
	 * The null drawing strategy.
	 * @since 3.0
	 */
	private static IDrawingStrategy fgNullStrategy= new AnnotationPainter.NullStrategy();

	/**
	 * The underline drawing strategy.
	 * @since 3.0
	 */
	private static ITextStyleStrategy fgUnderlineStrategy= new AnnotationPainter.UnderlineStrategy(SWT.UNDERLINE_SINGLE);

	/**
	 * The iBeam drawing strategy.
	 * @since 3.0
	 */
	private static IDrawingStrategy fgIBeamStrategy= new IBeamStrategy();

	/**
	 * The squiggles drawing strategy.
	 * @since 3.0
	 */
	private static ITextStyleStrategy fgSquigglesStrategy= new AnnotationPainter.UnderlineStrategy(SWT.UNDERLINE_SQUIGGLE);

	/**
	 * The error drawing strategy.
	 * @since 3.4
	 */
	private static ITextStyleStrategy fgProblemUnderlineStrategy= new AnnotationPainter.UnderlineStrategy(SWT.UNDERLINE_ERROR);


	/** The viewer */
	private ISourceViewer fSourceViewer;
	/** The viewer's overview ruler */
	private IOverviewRuler fOverviewRuler;
	/** The annotation access */
	private IAnnotationAccess fAnnotationAccess;
	/** The shared color manager */
	private ISharedTextColors fSharedTextColors;

	/** The editor's line painter */
	private CursorLinePainter fCursorLinePainter;
	/** The editor's margin ruler painter */
	private MarginPainter fMarginPainter;
	/** The editor's annotation painter */
	private AnnotationPainter fAnnotationPainter;
	/** The editor's peer character painter */
	private MatchingCharacterPainter fMatchingCharacterPainter;
	/** The character painter's pair matcher */
	private ICharacterPairMatcher fCharacterPairMatcher;

	/** Map with annotation type preference per annotation type */
	private Map<Object, AnnotationPreference> fAnnotationTypeKeyMap= new LinkedHashMap<>();
	/** Preference key for the cursor line highlighting */
	private String fCursorLinePainterEnableKey;
	/** Preference key for the cursor line background color */
	private String fCursorLinePainterColorKey;
	/** Preference key for the margin painter */
	private String fMarginPainterEnableKey;
	/** Preference key for the margin painter color */
	private String fMarginPainterColorKey;
	/** Preference key for the margin painter column */
	private String fMarginPainterColumnKey;
	/** Preference key for the matching character painter */
	private String fMatchingCharacterPainterEnableKey;
	/** Preference key for highlighting character at caret location */
	private String fMatchingCharacterPainterHighlightCharacterAtCaretLocationKey;
	/** Preference key for enclosing peer characters */
	private String fMatchingCharacterPainterEnclosingPeerCharactersKey;
	/** Preference key for the matching character painter color */
	private String fMatchingCharacterPainterColorKey;
	/** The property change listener */
	private IPropertyChangeListener fPropertyChangeListener;
	/** The preference store */
	private IPreferenceStore fPreferenceStore;


	/**
	 * Creates a new decoration support for the given viewer.
	 *
	 * @param sourceViewer the source viewer
	 * @param overviewRuler the viewer's overview ruler
	 * @param annotationAccess the annotation access
	 * @param sharedTextColors the shared text color manager
	 */
	public SourceViewerDecorationSupport(ISourceViewer sourceViewer, IOverviewRuler overviewRuler, IAnnotationAccess annotationAccess, ISharedTextColors sharedTextColors) {
		fSourceViewer= sourceViewer;
		fOverviewRuler= overviewRuler;
		fAnnotationAccess= annotationAccess;
		fSharedTextColors= sharedTextColors;
	}

	/**
	 * Installs this decoration support on the given preference store. It assumes
	 * that this support has completely been configured.
	 *
	 * @param store the preference store
	 */
	public void install(IPreferenceStore store) {

		fPreferenceStore= store;
		if (fPreferenceStore != null) {
			fPropertyChangeListener= this::handlePreferenceStoreChanged;
			fPreferenceStore.addPropertyChangeListener(fPropertyChangeListener);
		}

		updateTextDecorations();
		updateOverviewDecorations();
	}

	/**
	 * Updates the text decorations for all configured annotation types.
	 */
	private void updateTextDecorations() {

		StyledText widget= fSourceViewer.getTextWidget();
		if (widget == null || widget.isDisposed())
			return;

		if (areMatchingCharactersShown())
			showMatchingCharacters();
		else
			hideMatchingCharacters();

		if (isCursorLineShown())
			showCursorLine();
		else
			hideCursorLine();

		if (isMarginShown())
			showMargin();
		else
			hideMargin();

		Iterator<Object> e= fAnnotationTypeKeyMap.keySet().iterator();
		while (e.hasNext()) {
			Object type= e.next();
			if (areAnnotationsHighlighted(type) || areAnnotationsShown(type))
				showAnnotations(type, false);
			else
				hideAnnotations(type, false);

		}
		updateAnnotationPainter();
	}

	/**
	 * Returns the annotation decoration style used for the show in text preference for
	 * a given annotation type.
	 *
	 * @param annotationType the annotation type being looked up
	 * @return the decoration style for <code>type</code> or <code>null</code> if highlighting
	 * @since 3.0
	 */
	private Object getAnnotationDecorationType(Object annotationType) {
		if (areAnnotationsHighlighted(annotationType))
			return null;

		if (areAnnotationsShown(annotationType) && fPreferenceStore != null) {
			AnnotationPreference info= fAnnotationTypeKeyMap.get(annotationType);
			if (info != null) {
				String key= info.getTextStylePreferenceKey();
				if (key != null)
					return fPreferenceStore.getString(key);
				// legacy
				return AnnotationPreference.STYLE_SQUIGGLES;
			}
		}
		return AnnotationPreference.STYLE_NONE;
	}

	/**
	 * Updates the annotation overview for all configured annotation types.
	 */
	public void updateOverviewDecorations() {
		if (fOverviewRuler != null) {
			Iterator<Object> e= fAnnotationTypeKeyMap.keySet().iterator();
			while (e.hasNext()) {
				Object type= e.next();
				if (isAnnotationOverviewShown(type))
					showAnnotationOverview(type, false);
				else
					hideAnnotationOverview(type, false);
			}
			fOverviewRuler.update();
		}
	}

	/**
	 * Uninstalls this support from the preference store it has previously been
	 * installed on. If there is no such preference store, this call is without
	 * effect.
	 */
	public void uninstall() {

		if (fPreferenceStore != null) {
			fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);
			fPropertyChangeListener= null;
			fPreferenceStore= null;
		}
	}

	/**
	 * Disposes this decoration support. Internally calls
	 * <code>uninstall</code>.
	 */
	public void dispose() {
		uninstall();
		updateTextDecorations();
		updateOverviewDecorations();

		fOverviewRuler= null;

		// Painters got disposed in updateTextDecorations() or by the PaintManager
		fMatchingCharacterPainter= null;
		fAnnotationPainter= null;
		fCursorLinePainter= null;
		fMarginPainter= null;

		if (fAnnotationTypeKeyMap != null)
			fAnnotationTypeKeyMap.clear();
	}

	/**
	 * Sets the character pair matcher for the matching character painter.
	 *
	 * @param pairMatcher the character pair matcher
	 */
	public void setCharacterPairMatcher(ICharacterPairMatcher pairMatcher) {
		fCharacterPairMatcher= pairMatcher;
	}

	/**
	 * Sets the preference keys for the annotation painter.
	 *
	 * @param type the annotation type
	 * @param colorKey the preference key for the color
	 * @param editorKey the preference key for the presentation in the text area
	 * @param overviewRulerKey the preference key for the presentation in the overview  ruler
	 * @param layer the layer
	 */
	public void setAnnotationPainterPreferenceKeys(Object type, String colorKey, String editorKey, String overviewRulerKey, int layer) {
		AnnotationPreference info= new AnnotationPreference(type, colorKey, editorKey, overviewRulerKey, layer);
		fAnnotationTypeKeyMap.put(type, info);
	}

	/**
	 * Sets the preference info for the annotation painter.
	 * @param info the preference info to be set
	 */
	public void setAnnotationPreference(AnnotationPreference info) {
		fAnnotationTypeKeyMap.put(info.getAnnotationType(), info);
	}

	/**
	 * Sets the preference keys for the cursor line painter.
	 * @param enableKey the preference key for the cursor line painter
	 * @param colorKey the preference key for the color used by the cursor line
	 *        painter
	 */
	public void setCursorLinePainterPreferenceKeys(String enableKey, String colorKey) {
		fCursorLinePainterEnableKey= enableKey;
		fCursorLinePainterColorKey= colorKey;
	}

	/**
	 * Sets the preference keys for the margin painter.
	 * @param enableKey the preference key for the margin painter
	 * @param colorKey the preference key for the color used by the margin
	 *         painter
	 * @param columnKey the preference key for the margin column
	 */
	public void setMarginPainterPreferenceKeys(String enableKey, String colorKey, String columnKey) {
		fMarginPainterEnableKey= enableKey;
		fMarginPainterColorKey= colorKey;
		fMarginPainterColumnKey= columnKey;
	}

	/**
	 * Sets the preference keys for the matching character painter.
	 *
	 * <p>
	 * Use {@link #setMatchingCharacterPainterPreferenceKeys(String, String, String, String)} if
	 * highlighting of character at caret location or enclosing peer characters is required.
	 * </p>
	 *
	 * @param enableKey the preference key for the matching character painter
	 * @param colorKey the preference key for the color used by the matching character painter
	 */
	public void setMatchingCharacterPainterPreferenceKeys(String enableKey, String colorKey) {
		setMatchingCharacterPainterPreferenceKeys(enableKey, colorKey, null, null);
	}

	/**
	 * Sets the preference keys for the matching character painter.
	 *
	 * @param enableKey the preference key for the matching character painter
	 * @param colorKey the preference key for the color used by the matching character painter
	 * @param highlightCharacterAtCaretLocationKey the preference key for highlighting character at
	 *            caret location
	 * @param enclosingPeerCharactersKey the preference key for highlighting enclosing peer
	 *            characters
	 *
	 * @since 3.8
	 */
	public void setMatchingCharacterPainterPreferenceKeys(String enableKey, String colorKey, String highlightCharacterAtCaretLocationKey, String enclosingPeerCharactersKey) {
		fMatchingCharacterPainterEnableKey= enableKey;
		fMatchingCharacterPainterColorKey= colorKey;
		fMatchingCharacterPainterEnclosingPeerCharactersKey= enclosingPeerCharactersKey;
		fMatchingCharacterPainterHighlightCharacterAtCaretLocationKey= highlightCharacterAtCaretLocationKey;
	}

	/**
	 * Sets the symbolic font name that is used for computing the margin width.
	 *
	 * @param symbolicFontName
	 *            the symbolic font name
	 * @deprecated As of 4.8, this is no longer used
	 */
	@Deprecated
	public void setSymbolicFontName(String symbolicFontName) {
	}

	/**
	 * Returns the annotation preference for the given key.
	 *
	 * @param preferenceKey the preference key string
	 * @return the annotation preference
	 */
	private AnnotationPreference getAnnotationPreferenceInfo(String preferenceKey) {
		Iterator<AnnotationPreference> e= fAnnotationTypeKeyMap.values().iterator();
		while (e.hasNext()) {
			AnnotationPreference info= e.next();
			if (info != null && info.isPreferenceKey(preferenceKey))
				return info;
		}
		return null;
	}


	/*
	 * @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
	 */
	protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {

		String p= event.getProperty();

		if (fMatchingCharacterPainterEnableKey != null && fMatchingCharacterPainterEnableKey.equals(p) && fCharacterPairMatcher != null) {
			if (areMatchingCharactersShown())
				showMatchingCharacters();
			else
				hideMatchingCharacters();
			return;
		}

		if (fMatchingCharacterPainterHighlightCharacterAtCaretLocationKey != null && fMatchingCharacterPainterHighlightCharacterAtCaretLocationKey.equals(p)) {
			if (fMatchingCharacterPainter != null) {
				fMatchingCharacterPainter.setHighlightCharacterAtCaretLocation(isCharacterAtCaretLocationShown());
				fMatchingCharacterPainter.paint(IPainter.CONFIGURATION);
			}
			return;
		}

		if (fMatchingCharacterPainterEnclosingPeerCharactersKey != null && fMatchingCharacterPainterEnclosingPeerCharactersKey.equals(p)) {
			if (fMatchingCharacterPainter != null) {
				fMatchingCharacterPainter.setHighlightEnclosingPeerCharacters(areEnclosingPeerCharactersShown());
				fMatchingCharacterPainter.paint(IPainter.CONFIGURATION);
			}
			return;
		}

		if (fMatchingCharacterPainterColorKey != null && fMatchingCharacterPainterColorKey.equals(p)) {
			if (fMatchingCharacterPainter != null) {
				fMatchingCharacterPainter.setColor(getColor(fMatchingCharacterPainterColorKey));
				fMatchingCharacterPainter.paint(IPainter.CONFIGURATION);
			}
			return;
		}

		if (fCursorLinePainterEnableKey != null && fCursorLinePainterEnableKey.equals(p)) {
			if (isCursorLineShown())
				showCursorLine();
			else
				hideCursorLine();
			return;
		}

		if (fCursorLinePainterColorKey != null && fCursorLinePainterColorKey.equals(p)) {
			if (fCursorLinePainter != null) {
				hideCursorLine();
				showCursorLine();
			}
			return;
		}

		if (fMarginPainterEnableKey != null && fMarginPainterEnableKey.equals(p)) {
			if (isMarginShown())
				showMargin();
			else
				hideMargin();
			return;
		}

		if (fMarginPainterColorKey != null && fMarginPainterColorKey.equals(p)) {
			if (fMarginPainter != null) {
				fMarginPainter.setMarginRulerColor(getColor(fMarginPainterColorKey));
				fMarginPainter.paint(IPainter.CONFIGURATION);
			}
			return;
		}

		if (fMarginPainterColumnKey != null && fMarginPainterColumnKey.equals(p)) {
			if (fMarginPainter != null && fPreferenceStore != null) {
				fMarginPainter.setMarginRulerColumn(fPreferenceStore.getInt(fMarginPainterColumnKey));
				fMarginPainter.paint(IPainter.CONFIGURATION);
			}
			return;
		}

		AnnotationPreference info= getAnnotationPreferenceInfo(p);
		if (info != null) {

			if (info.getColorPreferenceKey().equals(p)) {
				Color color= getColor(info.getColorPreferenceKey());
				if (fAnnotationPainter != null) {
					fAnnotationPainter.setAnnotationTypeColor(info.getAnnotationType(), color);
					fAnnotationPainter.paint(IPainter.CONFIGURATION);
				}
				setAnnotationOverviewColor(info.getAnnotationType(), color);
				return;
			}

			Object type= info.getAnnotationType();
			if ((info.getTextPreferenceKey().equals(p) || info.getTextStylePreferenceKey() != null && info.getTextStylePreferenceKey().equals(p)) ||
					(info.getHighlightPreferenceKey() != null && info.getHighlightPreferenceKey().equals(p))) {
				if (areAnnotationsHighlighted(type) || areAnnotationsShown(type))
					showAnnotations(type, true);
				else
					hideAnnotations(type, true);
				return;
			}

			if (info.getOverviewRulerPreferenceKey().equals(p)) {
				if (isAnnotationOverviewShown(info.getAnnotationType()))
					showAnnotationOverview(info.getAnnotationType(), true);
				else
					hideAnnotationOverview(info.getAnnotationType(), true);
				return;
			}
		}

	}

	/**
	 * Returns the shared color for the given key.
	 *
	 * @param key the color key string
	 * @return the shared color for the given key
	 */
	private Color getColor(String key) {
		if (fPreferenceStore != null) {
			RGB rgb= PreferenceConverter.getColor(fPreferenceStore, key);
			return getColor(rgb);
		}
		return null;
	}

	/**
	 * Returns the shared color for the given RGB.
	 *
	 * @param rgb the RGB
	 * @return the shared color for the given RGB
	 */
	private Color getColor(RGB rgb) {
		return fSharedTextColors.getColor(rgb);
	}

	/**
	 * Returns the color of the given annotation type.
	 *
	 * @param annotationType the annotation type
	 * @return the color of the annotation type
	 */
	private Color getAnnotationTypeColor(Object annotationType) {
		AnnotationPreference info= fAnnotationTypeKeyMap.get(annotationType);
		if (info != null)
			return getColor( info.getColorPreferenceKey());
		return null;
	}



	/**
	 * Returns the layer of the given annotation type.
	 *
	 * @param annotationType the annotation type
	 * @return the layer
	 */
	private int getAnnotationTypeLayer(Object annotationType) {
		AnnotationPreference info= fAnnotationTypeKeyMap.get(annotationType);
		if (info != null)
			return info.getPresentationLayer();
		return 0;
	}

	/**
	 * Enables showing of matching characters.
	 */
	private void showMatchingCharacters() {
		if (fMatchingCharacterPainter == null) {
			if (fSourceViewer instanceof ITextViewerExtension2) {
				fMatchingCharacterPainter= new MatchingCharacterPainter(fSourceViewer, fCharacterPairMatcher);
				fMatchingCharacterPainter.setColor(getColor(fMatchingCharacterPainterColorKey));
				fMatchingCharacterPainter.setHighlightCharacterAtCaretLocation(isCharacterAtCaretLocationShown());
				fMatchingCharacterPainter.setHighlightEnclosingPeerCharacters(areEnclosingPeerCharactersShown());
				ITextViewerExtension2 extension= (ITextViewerExtension2) fSourceViewer;
				extension.addPainter(fMatchingCharacterPainter);
			}
		}
	}

	/**
	 * Disables showing of matching characters.
	 */
	private void hideMatchingCharacters() {
		if (fMatchingCharacterPainter != null) {
			if (fSourceViewer instanceof ITextViewerExtension2) {
				ITextViewerExtension2 extension= (ITextViewerExtension2) fSourceViewer;
				extension.removePainter(fMatchingCharacterPainter);
				fMatchingCharacterPainter.deactivate(true);
				fMatchingCharacterPainter.dispose();
				fMatchingCharacterPainter= null;
			}
		}
	}

	/**
	 * Tells whether matching characters are shown.
	 *
	 * @return <code>true</code> if the matching characters are shown
	 */
	private boolean areMatchingCharactersShown() {
		if (fPreferenceStore != null && fMatchingCharacterPainterEnableKey != null)
			return fPreferenceStore.getBoolean(fMatchingCharacterPainterEnableKey);
		return false;
	}

	/**
	 * Tells whether character at caret location is shown.
	 *
	 * @return <code>true</code> if character at caret location is shown
	 */
	private boolean isCharacterAtCaretLocationShown() {
		if (fPreferenceStore != null && fMatchingCharacterPainterHighlightCharacterAtCaretLocationKey != null)
			return fPreferenceStore.getBoolean(fMatchingCharacterPainterHighlightCharacterAtCaretLocationKey);
		return false;
	}

	/**
	 * Tells whether enclosing peer characters are shown.
	 *
	 * @return <code>true</code> if the enclosing peer characters are shown
	 */
	private boolean areEnclosingPeerCharactersShown() {
		if (fPreferenceStore != null && fMatchingCharacterPainterEnclosingPeerCharactersKey != null)
			return fPreferenceStore.getBoolean(fMatchingCharacterPainterEnclosingPeerCharactersKey);
		return false;
	}

	/**
	 * Shows the cursor line.
	 */
	private void showCursorLine() {
		if (fCursorLinePainter == null) {
			if (fSourceViewer instanceof ITextViewerExtension2) {
				fCursorLinePainter= new CursorLinePainter(fSourceViewer);
				fCursorLinePainter.setHighlightColor(getColor(fCursorLinePainterColorKey));
				ITextViewerExtension2 extension= (ITextViewerExtension2) fSourceViewer;
				extension.addPainter(fCursorLinePainter);
			}
		}
	}

	/**
	 * Hides the cursor line.
	 */
	private void hideCursorLine() {
		if (fCursorLinePainter != null) {
			if (fSourceViewer instanceof ITextViewerExtension2) {
				ITextViewerExtension2 extension= (ITextViewerExtension2) fSourceViewer;
				extension.removePainter(fCursorLinePainter);
				fCursorLinePainter.deactivate(true);
				fCursorLinePainter.dispose();
				fCursorLinePainter= null;
			}
		}
	}

	/**
	 * Tells whether the cursor line is shown.
	 *
	 * @return <code>true</code> if the cursor line is shown
	 */
	private boolean isCursorLineShown() {
		if (fPreferenceStore != null && fCursorLinePainterEnableKey != null)
			return fPreferenceStore.getBoolean(fCursorLinePainterEnableKey);
		return false;
	}

	/**
	 * Shows the margin.
	 */
	private void showMargin() {
		if (fMarginPainter == null) {
			if (fSourceViewer instanceof ITextViewerExtension2) {
				fMarginPainter= new MarginPainter(fSourceViewer);
				fMarginPainter.setMarginRulerColor(getColor(fMarginPainterColorKey));
				if (fPreferenceStore != null)
					fMarginPainter.setMarginRulerColumn(fPreferenceStore.getInt(fMarginPainterColumnKey));
				ITextViewerExtension2 extension= (ITextViewerExtension2) fSourceViewer;
				extension.addPainter(fMarginPainter);
			}
		}
	}

	/**
	 * Hides the margin.
	 */
	private void hideMargin() {
		if (fMarginPainter != null) {
			if (fSourceViewer instanceof ITextViewerExtension2) {
				ITextViewerExtension2 extension= (ITextViewerExtension2) fSourceViewer;
				extension.removePainter(fMarginPainter);
				fMarginPainter.deactivate(true);
				fMarginPainter.dispose();
				fMarginPainter= null;
			}
		}
	}

	/**
	 * Tells whether the margin is shown.
	 *
	 * @return <code>true</code> if the margin is shown
	 */
	private boolean isMarginShown() {
		if (fPreferenceStore != null && fMarginPainterEnableKey != null)
			return fPreferenceStore.getBoolean(fMarginPainterEnableKey);
		return false;
	}

	/**
	 * Enables annotations in the source viewer for the given annotation type.
	 *
	 * @param annotationType the annotation type
	 * @param updatePainter if <code>true</code> update the annotation painter
	 * @since 3.0
	 */
	private void showAnnotations(Object annotationType, boolean updatePainter) {
		if (fSourceViewer instanceof ITextViewerExtension2) {
			if (fAnnotationPainter == null) {
				fAnnotationPainter= createAnnotationPainter();
				if (fSourceViewer instanceof ITextViewerExtension4)
					((ITextViewerExtension4)fSourceViewer).addTextPresentationListener(fAnnotationPainter);
				ITextViewerExtension2 extension= (ITextViewerExtension2) fSourceViewer;
				extension.addPainter(fAnnotationPainter);
				if (fSourceViewer instanceof ISourceViewerExtension5)
					((ISourceViewerExtension5) fSourceViewer).setCodeMiningAnnotationPainter(fAnnotationPainter);
			}
			fAnnotationPainter.setAnnotationTypeColor(annotationType, getAnnotationTypeColor(annotationType));
			Object decorationType= getAnnotationDecorationType(annotationType);
			if (decorationType != null)
				fAnnotationPainter.addAnnotationType(annotationType, decorationType);
			else
				fAnnotationPainter.addHighlightAnnotationType(annotationType);

			if (updatePainter)
				updateAnnotationPainter();
		}
	}

	/**
	 * Creates and configures the annotation painter and configures.
	 * @return an annotation painter
	 * @since 3.0
	 */
	protected AnnotationPainter createAnnotationPainter() {
		AnnotationPainter painter= new AnnotationPainter(fSourceViewer, fAnnotationAccess);

		/*
		 * XXX:
		 * Could provide an extension point for drawing strategies,
		 * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=51498
		 */
		painter.addDrawingStrategy(AnnotationPreference.STYLE_NONE, fgNullStrategy);
		painter.addDrawingStrategy(AnnotationPreference.STYLE_IBEAM, fgIBeamStrategy);

		painter.addTextStyleStrategy(AnnotationPreference.STYLE_SQUIGGLES, fgSquigglesStrategy);
		painter.addTextStyleStrategy(AnnotationPreference.STYLE_PROBLEM_UNDERLINE, fgProblemUnderlineStrategy);
		painter.addTextStyleStrategy(AnnotationPreference.STYLE_BOX, fgBoxStrategy);
		painter.addTextStyleStrategy(AnnotationPreference.STYLE_DASHED_BOX, fgDashedBoxStrategy);
		painter.addTextStyleStrategy(AnnotationPreference.STYLE_UNDERLINE, fgUnderlineStrategy);

		return painter;
	}

	/**
	 * Updates the annotation painter.
	 * @since 3.0
	 */
	private void updateAnnotationPainter() {
		if (fAnnotationPainter == null)
			return;

		fAnnotationPainter.paint(IPainter.CONFIGURATION);
		if (!fAnnotationPainter.isPaintingAnnotations()) {
			if (fSourceViewer instanceof ITextViewerExtension2) {
				ITextViewerExtension2 extension= (ITextViewerExtension2) fSourceViewer;
				extension.removePainter(fAnnotationPainter);
			}
			if (fSourceViewer instanceof ITextViewerExtension4)
				((ITextViewerExtension4)fSourceViewer).removeTextPresentationListener(fAnnotationPainter);

			fAnnotationPainter.deactivate(true);
			fAnnotationPainter.dispose();
			fAnnotationPainter= null;
		}
	}

	/**
	 * Hides annotations in the source viewer for the given annotation type.
	 *
	 * @param annotationType the annotation type
	 * @param updatePainter if <code>true</code> update the annotation painter
	 * @since 3.0
	 */
	private void hideAnnotations(Object annotationType, boolean updatePainter) {
		if (fAnnotationPainter != null) {
			fAnnotationPainter.removeAnnotationType(annotationType);

			if (updatePainter) {
				updateAnnotationPainter();
			}
		}
	}

	/**
	 * Tells whether annotations are shown in the source viewer for the given type.
	 *
	 * @param annotationType the annotation type
	 * @return <code>true</code> if the annotations are shown
	 */
	private boolean areAnnotationsShown(Object annotationType) {
		if (fPreferenceStore != null) {
			AnnotationPreference info= fAnnotationTypeKeyMap.get(annotationType);
			if (info != null) {
				String key= info.getTextPreferenceKey();
				return key != null && fPreferenceStore.getBoolean(key);
			}
		}
		return false;
	}

	/**
	 * Tells whether annotations are highlighted in the source viewer for the given type.
	 *
	 * @param annotationType the annotation type
	 * @return <code>true</code> if the annotations are highlighted
	 * @since 3.0
	 */
	private boolean areAnnotationsHighlighted(Object annotationType) {
		if (fPreferenceStore != null) {
			AnnotationPreference info= fAnnotationTypeKeyMap.get(annotationType);
			if (info != null)
				return info.getHighlightPreferenceKey() != null && fPreferenceStore.getBoolean(info.getHighlightPreferenceKey());
		}
		return false;
	}

	/**
	 * Tells whether annotation overview is enabled for the given type.
	 *
	 * @param annotationType the annotation type
	 * @return <code>true</code> if the annotation overview is shown
	 */
	private boolean isAnnotationOverviewShown(Object annotationType) {
		if (fPreferenceStore != null && fOverviewRuler != null) {
			AnnotationPreference info= fAnnotationTypeKeyMap.get(annotationType);
			if (info != null)
				return fPreferenceStore.getBoolean(info.getOverviewRulerPreferenceKey());
		}
		return false;
	}

	/**
	 * Enable annotation overview for the given annotation type.
	 *
	 * @param annotationType the annotation type
	 * @param update <code>true</code> if the overview should be updated
	 */
	private void showAnnotationOverview(Object annotationType, boolean update) {
		if (fOverviewRuler != null) {
			fOverviewRuler.setAnnotationTypeColor(annotationType, getAnnotationTypeColor(annotationType));
			fOverviewRuler.setAnnotationTypeLayer(annotationType, getAnnotationTypeLayer(annotationType));
			fOverviewRuler.addAnnotationType(annotationType);
			if (update)
				fOverviewRuler.update();
		}
	}

	/**
	 * Hides the annotation overview for the given type.
	 * @param annotationType the annotation type
	 * @param update <code>true</code> if the overview should be updated
	 */
	private void hideAnnotationOverview(Object annotationType, boolean update) {
		if (fOverviewRuler != null) {
			fOverviewRuler.removeAnnotationType(annotationType);
			if (update)
				fOverviewRuler.update();
		}
	}

	/**
	 * Hides the annotation overview.
	 */
	public void hideAnnotationOverview() {
		if (fOverviewRuler != null) {
			Iterator<Object> e= fAnnotationTypeKeyMap.keySet().iterator();
			while (e.hasNext())
				fOverviewRuler.removeAnnotationType(e.next());
			fOverviewRuler.update();
		}
	}

	/**
	 * Sets the annotation overview color for the given annotation type.
	 *
	 * @param annotationType the annotation type
	 * @param color the color
	 */
	private void setAnnotationOverviewColor(Object annotationType, Color color) {
		if (fOverviewRuler != null) {
			fOverviewRuler.setAnnotationTypeColor(annotationType, color);
			fOverviewRuler.update();
		}
	}
}
