blob: bac799c2f160f98051fac0584dcf773661f6bd77 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2006 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
* Jens Lukowski/Innoopract - initial renaming/restructuring
*
*******************************************************************************/
package org.eclipse.wst.sse.ui.internal.provisional.style;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.custom.LineStyleEvent;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.util.Debug;
import org.eclipse.wst.sse.ui.internal.ExtendedConfigurationBuilder;
import org.eclipse.wst.sse.ui.internal.Logger;
import org.eclipse.wst.sse.ui.internal.SSEUIPlugin;
import org.eclipse.wst.sse.ui.internal.preferences.EditorPreferenceNames;
import org.eclipse.wst.sse.ui.internal.util.EditorUtility;
/**
* This class is to directly mediate between the Structured Document data
* structure and the text widget's text and events. It assumes there only the
* model is interested in text events, and all other views will work from that
* model. Changes to the text widgets input can cause changes in the model,
* which in turn cause changes to the widget's display.
*
*/
public class Highlighter implements IHighlighter {
/**
* A utility class to do various color manipulations
*/
private class YUV_RGBConverter {
/**
* This class "holds" the YUV values corresponding to RGB color
*/
private class YUV {
class NormalizedRGB {
double blue;
double green;
private final double maxRGB = 256.0;
double red;
public NormalizedRGB(RGB rgb) {
// first normalize to between 0 - 1
red = rgb.red / maxRGB;
green = rgb.green / maxRGB;
blue = rgb.blue / maxRGB;
red = gammaNormalized(red);
green = gammaNormalized(green);
blue = gammaNormalized(blue);
}
}
private NormalizedRGB normalizedRGB;
RGB originalRGB;
private double u = -1;
private double v = -1;
private double y = -1;
private YUV() {
super();
}
public YUV(double y, double u, double v) {
this();
this.y = y;
this.u = u;
this.v = v;
}
public YUV(RGB rgb) {
this();
originalRGB = rgb;
normalizedRGB = new NormalizedRGB(rgb);
// force calculations
getY();
getV();
getU();
}
/**
* normalize to "average" gamma 2.2222 or 1/0.45
*/
double gammaNormalized(double colorComponent) {
if (colorComponent < 0.018) {
return colorComponent * 0.45;
}
else {
return 1.099 * Math.pow(colorComponent, 0.45) - 0.099;
}
}
/**
* @return RGB based on original RGB and current YUV values;
*/
public RGB getRGB() {
RGB result = null;
double r = getY() + 1.14 * getV();
double g = getY() - 0.395 * getU() - 0.58 * getV();
double b = getY() + 2.032 * getU();
int red = (int) (inverseGammaNormalized(r) * 256);
int green = (int) (inverseGammaNormalized(g) * 256);
int blue = (int) (inverseGammaNormalized(b) * 256);
if (red < 0)
red = 0;
else if (red > 255)
red = 255;
if (green < 0)
green = 0;
else if (green > 255)
green = 255;
if (blue < 0)
blue = 0;
else if (blue > 255)
blue = 255;
result = new RGB(red, green, blue);
return result;
}
public double getU() {
if (u == -1) {
u = 0.4949 * (normalizedRGB.blue - getY());
}
return u;
}
public double getV() {
if (v == -1) {
v = 0.877 * (normalizedRGB.red - getY());
}
return v;
}
public double getY() {
if (y == -1) {
y = 0.299 * normalizedRGB.red + 0.587 * normalizedRGB.green + 0.114 * normalizedRGB.blue;
}
return y;
}
double inverseGammaNormalized(double colorComponent) {
if (colorComponent < 0.018) {
return colorComponent * .222;
}
else {
return Math.pow(((.9099 * colorComponent + 0.09)), 2.22);
}
}
}
public YUV_RGBConverter() {
super();
}
public double calculateYComponent(Color targetColor) {
return new YUV(targetColor.getRGB()).getY();
}
public RGB transformRGB(RGB originalRGB, double scaleFactor, double target) {
RGB transformedRGB = null;
// CCIR601 yuv = new CCIR601(originalRGB);
YUV yuv = new YUV(originalRGB);
double y = yuv.getY();
// zero is black, one is white
if (y < target) {
// is "dark" make lighter
y = y + ((target - y) * scaleFactor);
}
else {
// is "light" make darker
y = y - ((y - target) * scaleFactor);
}
// yuv.setY(y);
YUV newYUV = new YUV(y, yuv.getU(), yuv.getV());
// CCIR601 newYUV = new CCIR601(y, yuv.getCb601(),
// yuv.getCr601());
transformedRGB = newYUV.getRGB();
return transformedRGB;
}
public RGB transformRGBToGrey(RGB originalRGB, double scaleFactor, double target) {
RGB transformedRGB = null;
// we left the "full" API method signature, but this
// version does not take into account originalRGB, though
// it might someday.
// for now, we'll simply make the new RGB grey, either a little
// lighter, or a little darker than background.
double y = 0;
double mid = 0.5;
// zero is black, one is white
if (target < mid) {
// is "dark" make lighter
y = target + scaleFactor;
}
else {
// is "light" make darker
y = target - scaleFactor;
}
int c = (int) Math.round(y * 255);
// just to gaurd against mis-use, or scale's values greater
// than mid point (and possibly rounding error)
if (c > 255)
c = 255;
if (c < 0)
c = 0;
transformedRGB = new RGB(c, c, c);
return transformedRGB;
}
}
private final boolean DEBUG = false;
private final StyleRange[] EMPTY_STYLE_RANGE = new StyleRange[0];
static final String LINE_STYLE_PROVIDER_EXTENDED_ID = "linestyleprovider"; //$NON-NLS-1$
private static final int MAX_NUMBER_STYLES = 500;
private static final int LEFT_STYLES_SIZE = 200;
private static final int RIGHT_STYLES_SIZE = 200;
private static final int MIDDLE_STYLES_SIZE = 1;
private IPropertyChangeListener fForegroundScaleListener = new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (EditorPreferenceNames.READ_ONLY_FOREGROUND_SCALE.equals(event.getProperty())) {
IPreferenceStore editorStore = SSEUIPlugin.getDefault().getPreferenceStore();
readOnlyForegroundScaleFactor = editorStore.getInt(EditorPreferenceNames.READ_ONLY_FOREGROUND_SCALE);
disposeColorTable();
refreshDisplay();
}
}
};
private List fHoldStyleResults;
private String fPartitioning = IDocumentExtension3.DEFAULT_PARTITIONING;
private int fSavedLength = -1;
private int fSavedOffset = -1;
private StyleRange[] fSavedRanges = null;
private IStructuredDocument fStructuredDocument;
private Map fTableOfProviders;
private Map fExtendedProviders;
protected final LineStyleProvider NOOP_PROVIDER = new LineStyleProviderForNoOp();
private double readOnlyBackgroundScaleFactor = 10;
private Hashtable readOnlyColorTable;
double readOnlyForegroundScaleFactor = 30;
private YUV_RGBConverter rgbConverter;
private ITextViewer textViewer;
private StyledText textWidget;
public Highlighter() {
super();
// in the 'limitSize' method, we make this strong assumption, so, will check here,
// so if tweaked in future, we'll get a quick reminder.
if (LEFT_STYLES_SIZE + MIDDLE_STYLES_SIZE + RIGHT_STYLES_SIZE > MAX_NUMBER_STYLES) {
throw new IllegalStateException("Highligher constants are not defined correctly"); //$NON-NLS-1$
}
}
protected void addEmptyRange(int start, int length, Collection holdResults) {
StyleRange result = new StyleRange();
result.start = start;
result.length = length;
holdResults.add(result);
}
/**
* Registers a given line style provider for a particular partition type.
* If there is already a line style provider registered for this type, the
* new line style provider is registered instead of the old one.
*
* @param partitionType
* the partition type under which to register
* @param the
* line style provider to register, or <code>null</code> to
* remove an existing one
*/
public void addProvider(String partitionType, LineStyleProvider provider) {
getTableOfProviders().put(partitionType, provider);
}
/**
* Adjust the style ranges' start and length so that they refer to the
* textviewer widget's range instead of the textviewer's document range.
*
* @param ranges
* @param adjustment
*/
protected void adjust(StyleRange[] ranges, int adjustment) {
ITextViewer viewer = getTextViewer();
if (adjustment != 0) {
// just use the adjustment value
// convert document regions back to widget regions
for (int i = 0; i < ranges.length; i++) {
// just adjust the range using the given adjustment
ranges[i].start += adjustment;
}
}
else if (viewer instanceof ITextViewerExtension5) {
// use ITextViewerExtension5
ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
// convert document regions back to widget regions
for (int i = 0; i < ranges.length; i++) {
// get document range, taking into account folding
// regions in viewer
IRegion region = extension.modelRange2WidgetRange(new Region(ranges[i].start, ranges[i].length));
if (region != null) {
ranges[i].start = region.getOffset();
ranges[i].length = region.getLength();
} // else what happens if region is not found?!
}
}
}
/**
* @deprecated - Read Only areas have unchanged background colors
*/
void adjustBackground(StyleRange styleRange) {
RGB oldRGB = null;
Color oldColor = styleRange.background;
if (oldColor == null) {
oldColor = getTextWidget().getBackground();
}
oldRGB = oldColor.getRGB();
Color newColor = getCachedColorFor(oldRGB);
if (newColor == null) {
double target = getRGBConverter().calculateYComponent(oldColor);
// if background is "light" make it darker, and vice versa
if (target < 0.5)
target = 1.0;
else
target = 0.0;
RGB newRGB = getRGBConverter().transformRGB(oldRGB, readOnlyBackgroundScaleFactor / 100.0, target);
cacheColor(oldRGB, newRGB);
newColor = getCachedColorFor(oldRGB);
}
styleRange.background = newColor;
}
private void adjustForeground(StyleRange styleRange) {
RGB oldRGB = null;
// Color oldColor = styleRange.foreground;
Color oldColor = styleRange.background;
if (oldColor == null) {
// oldRGB = getTextWidget().getForeground().getRGB();
oldColor = getTextWidget().getBackground();
oldRGB = oldColor.getRGB();
}
else {
oldRGB = oldColor.getRGB();
}
Color newColor = getCachedColorFor(oldRGB);
if (newColor == null) {
// make text "closer to" background lumanence
double target = getRGBConverter().calculateYComponent(oldColor);
RGB newRGB = getRGBConverter().transformRGBToGrey(oldRGB, readOnlyForegroundScaleFactor / 100.0, target);
// save conversion, so calculations only need to be done once
cacheColor(oldRGB, newRGB);
newColor = getCachedColorFor(oldRGB);
}
styleRange.foreground = newColor;
}
/**
* Cache read-only color.
*
* @param oldRGB
* @param newColor
*/
private void cacheColor(RGB oldRGB, RGB newColor) {
if (readOnlyColorTable == null) {
readOnlyColorTable = new Hashtable();
}
readOnlyColorTable.put(oldRGB, newColor);
}
/**
* @param result
* @return
*/
private StyleRange[] convertReadOnlyRegions(StyleRange[] result, int start, int length) {
IStructuredDocument structuredDocument = getDocument();
/**
* (dmw) For client/provider simplicity (and consistent look and feel)
* we'll handle readonly regions in one spot, here in the Highlighter.
* Currently it is a fair assumption that each readonly region will be
* on an ITextRegion boundary, so we combine consecutive styles when
* found to be equivalent. Plus, for now, we'll just adjust
* foreground. Eventually will use a "dimming" algrorithm to adjust
* color's satuation/brightness.
*/
if (structuredDocument.containsReadOnly(start, length)) {
// something is read-only in the line, so go through each style,
// and adjust
for (int i = 0; i < result.length; i++) {
StyleRange styleRange = result[i];
if (structuredDocument.containsReadOnly(styleRange.start, styleRange.length)) {
adjustForeground(styleRange);
}
}
}
return result;
}
/**
* Clear out the readOnlyColorTable
*/
void disposeColorTable() {
if (readOnlyColorTable != null) {
readOnlyColorTable.clear();
}
readOnlyColorTable = null;
}
/**
* This method is just to get existing read-only colors.
*/
private Color getCachedColorFor(RGB oldRGB) {
Color result = null;
if (readOnlyColorTable != null) {
RGB readOnlyRGB = (RGB) readOnlyColorTable.get(oldRGB);
result = EditorUtility.getColor(readOnlyRGB);
}
return result;
}
protected IStructuredDocument getDocument() {
return fStructuredDocument;
}
/**
* Adjust the given widget offset and length so that they are the
* textviewer document's offset and length, taking into account what is
* actually visible in the document.
*
* @param offset
* @param length
* @return a region containing the offset and length within the
* textviewer's document or null if the offset is not within the
* document
*/
private IRegion getDocumentRangeFromWidgetRange(int offset, int length) {
IRegion styleRegion = null;
ITextViewer viewer = getTextViewer();
if (viewer instanceof ITextViewerExtension5) {
// get document range, taking into account folding regions in
// viewer
ITextViewerExtension5 extension = (ITextViewerExtension5) viewer;
styleRegion = extension.widgetRange2ModelRange(new Region(offset, length));
}
else {
// get document range, taking into account viewer visible region
// get visible region in viewer
IRegion vr = null;
if (viewer != null)
vr = viewer.getVisibleRegion();
else
vr = new Region(0, getDocument().getLength());
// if offset is not within visible region, then we don't really
// care
if (offset <= vr.getLength()) {
// Adjust the offset to be within visible region
styleRegion = new Region(offset + vr.getOffset(), length);
}
}
return styleRegion;
}
private Map getExtendedProviders() {
if (fExtendedProviders == null) {
fExtendedProviders = new HashMap(3);
}
return fExtendedProviders;
}
/**
* Method getProviderFor.
*
* @param typedRegion
* @return LineStyleProvider
*/
private LineStyleProvider getProviderFor(ITypedRegion typedRegion) {
String type = typedRegion.getType();
LineStyleProvider result = (LineStyleProvider) fTableOfProviders.get(type);
if (result == null) {
// NOT YET FINALIZED - DO NOT CONSIDER AS API
synchronized (getExtendedProviders()) {
if (!getExtendedProviders().containsKey(type)) {
LineStyleProvider provider = (LineStyleProvider) ExtendedConfigurationBuilder.getInstance().getConfiguration(LINE_STYLE_PROVIDER_EXTENDED_ID, type);
getExtendedProviders().put(type, provider);
if (provider != null) {
provider.init(getDocument(), this);
}
result = provider;
}
else {
result = (LineStyleProvider) getExtendedProviders().get(type);
}
}
}
if (result == null) {
result = NOOP_PROVIDER;
}
return result;
}
private YUV_RGBConverter getRGBConverter() {
if (rgbConverter == null) {
rgbConverter = new YUV_RGBConverter();
}
return rgbConverter;
}
private Map getTableOfProviders() {
if (fTableOfProviders == null) {
fTableOfProviders = new HashMap();
}
return fTableOfProviders;
}
/**
* Returns the textViewer.
*
* @return ITextViewer
*/
public ITextViewer getTextViewer() {
return textViewer;
}
/**
* @return
*/
protected StyledText getTextWidget() {
return textWidget;
}
/**
* Installs highlighter support on the given text viewer.
*
* @param textViewer
* the text viewer on which content assist will work
*/
public void install(ITextViewer newTextViewer) {
this.textViewer = newTextViewer;
IPreferenceStore editorStore = SSEUIPlugin.getDefault().getPreferenceStore();
editorStore.addPropertyChangeListener(fForegroundScaleListener);
readOnlyForegroundScaleFactor = editorStore.getInt(EditorPreferenceNames.READ_ONLY_FOREGROUND_SCALE);
if (textWidget != null) {
textWidget.removeLineStyleListener(this);
}
textWidget = newTextViewer.getTextWidget();
if (textWidget != null) {
textWidget.addLineStyleListener(this);
}
refreshDisplay();
}
public StyleRange[] lineGetStyle(int eventLineOffset, int eventLineLength) {
StyleRange[] eventStyles = EMPTY_STYLE_RANGE;
try {
if (getDocument() == null || eventLineLength == 0) {
// getDocument() == null
// during initialization, this is sometimes called before our
// structured
// is set, in which case we set styles to be the empty style
// range
// (event.styles can not be null)
// eventLineLength == 0
// we sometimes get odd requests from the very last CRLF in
// the
// document
// it has no length, and there is no node for it!
eventStyles = EMPTY_STYLE_RANGE;
}
else {
/*
* LineStyleProviders work using absolute document offsets. To
* support visible regions, adjust the requested range up to
* the full document offsets.
*/
IRegion styleRegion = getDocumentRangeFromWidgetRange(eventLineOffset, eventLineLength);
if (styleRegion != null) {
int start = styleRegion.getOffset();
int length = styleRegion.getLength();
ITypedRegion[] partitions = TextUtilities.computePartitioning(getDocument(), fPartitioning, start, length, false);
eventStyles = prepareStyleRangesArray(partitions, start, length);
/*
* If there is a subtext offset, the style ranges must be
* adjusted to the expected offsets just check if
* eventLineOffset is different than start then adjust,
* otherwise u can leave it alone unless there is special
* handling for itextviewerextension5?
*/
if (start != eventLineOffset) {
int offset = 0;
// figure out visible region to use for adjustment
// only adjust if need to
if (!(getTextViewer() instanceof ITextViewerExtension5)) {
IRegion vr = getTextViewer().getVisibleRegion();
if (vr != null) {
offset = vr.getOffset();
}
}
adjust(eventStyles, -offset);
}
eventStyles = limitSize(eventStyles);
// for debugging only
if (DEBUG) {
if (!valid(eventStyles, eventLineOffset, eventLineLength)) {
Logger.log(Logger.WARNING, "Highlighter::lineGetStyle found invalid styles at offset " + eventLineOffset); //$NON-NLS-1$
}
}
}
}
}
catch (Exception e) {
// if ANY exception occurs during highlighting,
// just return "no highlighting"
eventStyles = EMPTY_STYLE_RANGE;
if (Debug.syntaxHighlighting) {
System.out.println("Exception during highlighting!"); //$NON-NLS-1$
}
}
return eventStyles;
}
/**
* This method is to centralize the logic in limiting the overall number of style ranges
* that make it to the styled text widget.
*
* Too many styles sent to StyledText results in apparent, but not real,
* hangs of Eclipse Display thread. See
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=108806
*
* @param eventStyles
* @return
*/
private StyleRange[] limitSize(StyleRange[] eventStyles) {
// quick return with same object if not modification needed
if (eventStyles.length < MAX_NUMBER_STYLES) {
return eventStyles;
}
else {
// we could just take the easy way out and truncate, but will
// be much better appearing if both the start of the line and the
// end of the line are displayed with styles. Since these are both
// the parts of the line a user is likely to look at. The middle of the
// line will still be "plain". Presumably, the user would re-format the
// file to avoid long lines, so unlikely to see the middle.
StyleRange[] newRanges = new StyleRange[LEFT_STYLES_SIZE + RIGHT_STYLES_SIZE + MIDDLE_STYLES_SIZE];
System.arraycopy(eventStyles, 0, newRanges, 0, LEFT_STYLES_SIZE);
//
// do end, before we do middle
System.arraycopy(eventStyles, eventStyles.length-RIGHT_STYLES_SIZE, newRanges, LEFT_STYLES_SIZE + MIDDLE_STYLES_SIZE, RIGHT_STYLES_SIZE);
//
// technically, we should compute the exact middle as one big style range,
// with default colors and styles, so if someone does actually type or work with
// documnet as is, will still be correct.
//
StyleRange allBlank = new StyleRange();
StyleRange lastKnown = newRanges[LEFT_STYLES_SIZE - 1];
allBlank.start = lastKnown.start + lastKnown.length;
StyleRange nextKnown = newRanges[LEFT_STYLES_SIZE + MIDDLE_STYLES_SIZE + 1];
allBlank.length = nextKnown.start - allBlank.start;
newRanges[LEFT_STYLES_SIZE] = allBlank;
return newRanges;
}
}
/**
* A passthrough method that extracts relevant data from the
* LineStyleEvent and passes it along. This method was separated for
* performance testing purposes.
*
* @see org.eclipse.swt.custom.LineStyleListener#lineGetStyle(LineStyleEvent)
*/
public void lineGetStyle(LineStyleEvent event) {
int offset = event.lineOffset;
int length = event.lineText.length();
/*
* For some reason, we are sometimes asked for the same style range
* over and over again. This was found to happen during 'revert' of a
* file with one line in it that is 40K long! So, while we don't know
* root cause, caching the styled ranges in case the exact same
* request is made multiple times seems like cheap insurance.
*/
if (offset == fSavedOffset && length == fSavedLength && fSavedRanges != null) {
event.styles = fSavedRanges;
}
else {
// need to assign this array here, or else the field won't get
// updated
event.styles = lineGetStyle(offset, length);
// now saved "cached data" for repeated requests which are exaclty
// same
fSavedOffset = offset;
fSavedLength = length;
fSavedRanges = event.styles;
}
}
/**
* Note: its very important this method never return null, which is why
* the final null check is in a finally clause
*/
protected StyleRange[] prepareStyleRangesArray(ITypedRegion[] partitions, int start, int length) {
StyleRange[] result = EMPTY_STYLE_RANGE;
if (fHoldStyleResults == null) {
fHoldStyleResults = new ArrayList(partitions.length);
}
else {
fHoldStyleResults.clear();
}
// TODO: make some of these instance variables to prevent creation on
// stack
LineStyleProvider currentLineStyleProvider = null;
boolean handled = false;
for (int i = 0; i < partitions.length; i++) {
ITypedRegion currentPartition = partitions[i];
currentLineStyleProvider = getProviderFor(currentPartition);
currentLineStyleProvider.init(getDocument(), this);
handled = currentLineStyleProvider.prepareRegions(currentPartition, currentPartition.getOffset(), currentPartition.getLength(), fHoldStyleResults);
if (Debug.syntaxHighlighting && !handled) {
System.out.println("Did not handle highlighting in Highlighter inner while"); //$NON-NLS-1$
}
}
int resultSize = fHoldStyleResults.size();
if (resultSize > 0) {
result = (StyleRange[]) fHoldStyleResults.toArray(new StyleRange[fHoldStyleResults.size()]);
}
else {
result = EMPTY_STYLE_RANGE;
}
result = convertReadOnlyRegions(result, start, length);
return result;
}
public void refreshDisplay() {
if (textWidget != null && !textWidget.isDisposed())
textWidget.redraw();
}
/**
*/
public void refreshDisplay(int start, int length) {
if (textWidget != null && !textWidget.isDisposed())
textWidget.redrawRange(start, length, true);
}
public void removeProvider(String partitionType) {
getTableOfProviders().remove(partitionType);
}
public void setDocument(IStructuredDocument structuredDocument) {
fStructuredDocument = structuredDocument;
}
public void setDocumentPartitioning(String partitioning) {
if (partitioning != null) {
fPartitioning = partitioning;
}
else {
fPartitioning = IDocumentExtension3.DEFAULT_PARTITIONING;
}
}
/**
* Uninstalls highlighter support from the text viewer it has previously
* be installed on.
*/
public void uninstall() {
if (textWidget != null && !textWidget.isDisposed()) {
textWidget.removeLineStyleListener(this);
}
textWidget = null;
Collection providers = getTableOfProviders().values();
Iterator iterator = providers.iterator();
while (iterator.hasNext()) {
LineStyleProvider lineStyleProvider = (LineStyleProvider) iterator.next();
lineStyleProvider.release();
// this remove probably isn't strictly needed, since
// typically highlighter instance as a whole will go
// away ... but in case that ever changes, this seems like
// a better style.
iterator.remove();
}
synchronized (getExtendedProviders()) {
providers = new ArrayList(getExtendedProviders().values());
getExtendedProviders().clear();
}
iterator = providers.iterator();
while (iterator.hasNext()) {
LineStyleProvider lineStyleProvider = (LineStyleProvider) iterator.next();
if (lineStyleProvider != null) {
lineStyleProvider.release();
iterator.remove();
}
}
IPreferenceStore editorStore = SSEUIPlugin.getDefault().getPreferenceStore();
editorStore.removePropertyChangeListener(fForegroundScaleListener);
disposeColorTable();
// clear out cached variables (d282894)
fSavedOffset = -1;
fSavedLength = -1;
fSavedRanges = null;
}
/**
* Purely a debugging aide.
*/
private boolean valid(StyleRange[] eventStyles, int startOffset, int lineLength) {
boolean result = false;
if (eventStyles != null) {
if (eventStyles.length > 0) {
StyleRange first = eventStyles[0];
StyleRange last = eventStyles[eventStyles.length - 1];
if (startOffset > first.start) {
result = false;
}
else {
int lineEndOffset = startOffset + lineLength;
int lastOffset = last.start + last.length;
if (lastOffset > lineEndOffset) {
result = false;
}
else {
result = true;
}
}
}
else {
// a zero length array is ok
result = true;
}
}
return result;
}
}