net.sf.eclipsensis.editor.text.NSISTextUtility.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.eclipsensis.editor.text.NSISTextUtility.java

Source

/*******************************************************************************
 * Copyright (c) 2004-2010 Sunil Kamath (IcemanK).
 * All rights reserved.
 * This program is made available under the terms of the Common Public License
 * v1.0 which is available at http://www.eclipse.org/legal/cpl-v10.html
 *
 * Contributors:
 *     Sunil Kamath (IcemanK) - initial API and implementation
 *******************************************************************************/
package net.sf.eclipsensis.editor.text;

import java.util.*;

import net.sf.eclipsensis.*;
import net.sf.eclipsensis.util.Common;

import org.eclipse.jface.preference.*;
import org.eclipse.jface.resource.*;
import org.eclipse.jface.text.*;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.rules.*;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.util.*;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.AbstractTextEditor;

public class NSISTextUtility implements INSISConstants {
    public static final IRegion EMPTY_REGION = new Region(0, 0);
    private static final String[] cValidPartitionTypes = { IDocument.DEFAULT_CONTENT_TYPE,
            NSISPartitionScanner.NSIS_STRING };
    public static final int COMPUTE_OFFSET_HOVER_LOCATION = 1;
    public static final int COMPUTE_OFFSET_CURSOR_LOCATION = 2;
    public static final int COMPUTE_OFFSET_CARET_LOCATION = 4;
    public static final int COMPUTE_OFFSET_ANY = COMPUTE_OFFSET_HOVER_LOCATION | COMPUTE_OFFSET_CURSOR_LOCATION
            | COMPUTE_OFFSET_CARET_LOCATION;

    private NSISTextUtility() {
    }

    public static void setupPartitioning(IDocument document) {
        if (document instanceof IDocumentExtension3) {
            IDocumentExtension3 extension3 = (IDocumentExtension3) document;
            IDocumentPartitioner partitioner = new FastPartitioner(new NSISPartitionScanner(),
                    NSISPartitionScanner.NSIS_PARTITION_TYPES);
            extension3.setDocumentPartitioner(NSISPartitionScanner.NSIS_PARTITIONING, partitioner);
            partitioner.connect(document);
        }
    }

    public static int computeOffset(ISourceViewer sourceViewer, int type) {
        if (!(sourceViewer instanceof ITextViewerExtension2)) {
            return -1;
        }

        ITextViewerExtension2 textViewerExtension2 = (ITextViewerExtension2) sourceViewer;

        StyledText widget = sourceViewer.getTextWidget();
        int offset = -1;
        if ((type & COMPUTE_OFFSET_HOVER_LOCATION) > 0) {
            // does a text hover exist?
            ITextHover textHover = textViewerExtension2.getCurrentTextHover();
            if (textHover == null) {
                offset = -1;
            } else {
                Point hoverEventLocation = textViewerExtension2.getHoverEventLocation();
                offset = computeOffsetAtLocation(sourceViewer, hoverEventLocation.x, hoverEventLocation.y);
            }
        }
        if (offset < 0 && (type & COMPUTE_OFFSET_CARET_LOCATION) > 0) {
            offset = widget.getCaretOffset();
            if (offset >= 0) {
                return offset;
            }
        }
        if (offset < 0 && (type & COMPUTE_OFFSET_CURSOR_LOCATION) > 0) {
            Point p = null;
            p = widget.toControl(widget.getDisplay().getCursorLocation());
            try {
                offset = computeOffsetAtLocation(sourceViewer, p.x, p.y);
            } catch (Exception e) {
                offset = -1;
            }
        }
        return offset;
    }

    public static int computeOffsetAtLocation(ISourceViewer sourceViewer, int x, int y) {
        StyledText styledText = sourceViewer.getTextWidget();
        IDocument document = sourceViewer.getDocument();

        if (document == null) {
            return -1;
        }

        try {
            int widgetLocation = styledText.getOffsetAtLocation(new Point(x, y));
            if (sourceViewer instanceof ITextViewerExtension5) {
                ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer;
                return extension.widgetOffset2ModelOffset(widgetLocation);
            } else {
                IRegion visibleRegion = sourceViewer.getVisibleRegion();
                return widgetLocation + visibleRegion.getOffset();
            }
        } catch (IllegalArgumentException e) {
            return -1;
        }
    }

    public static IRegion intersection(IRegion region1, IRegion region2) {
        int start1 = region1.getOffset();
        int end1 = start1 + region1.getLength() - 1;
        int start2 = region2.getOffset();
        int end2 = start2 + region2.getLength() - 1;
        if (start1 >= 0 && end1 >= 0 && start2 >= 0 && end2 >= 0) {
            if (start1 < start2) {
                if (end1 >= start2) {
                    return (end2 <= end1 ? region2 : new Region(start2, (end1 - start2 + 1)));
                }
            } else {
                if (start1 <= end2) {
                    return (end2 < end1 ? new Region(start1, (end2 - start1 + 1)) : region1);
                }
            }

        }
        return EMPTY_REGION;
    }

    public static ITypedRegion[][] getNSISLines(IDocument doc) {
        return getNSISLines(doc, getNSISPartitions(doc));
    }

    public static boolean contains(IRegion region, int offset) {
        return (region != null && offset >= region.getOffset() && offset < region.getOffset() + region.getLength());
    }

    public static int findRegion(IRegion[] regions, int offset) {
        if (!Common.isEmptyArray(regions)) {
            int low = 0;
            int high = regions.length - 1;

            while (low <= high) {
                int mid = (low + high) >> 1;
                IRegion midVal = regions[mid];
                if (contains(midVal, offset)) {
                    return mid;
                } else if (midVal.getOffset() > offset) {
                    high = mid - 1;
                } else if (midVal.getOffset() < offset) {
                    low = mid + 1;
                } else {
                    break;
                }
            }
        }
        return -1;
    }

    public static ITypedRegion[][] getNSISLines(IDocument doc, int offset) {
        ITypedRegion[] typedRegions = null;
        try {
            int linenum = doc.getLineOfOffset(offset);
            IRegion line = doc.getLineInformation(linenum);
            ITypedRegion typedRegion = getNSISPartitionAtOffset(doc, line.getOffset());
            if (!isValidRegionType(typedRegion.getType(), cValidPartitionTypes)) {
                int startOffset = typedRegion.getOffset() + typedRegion.getLength();
                while (!isValidRegionType(typedRegion.getType(), cValidPartitionTypes)) {
                    if (contains(typedRegion, offset)) {
                        return new ITypedRegion[0][];
                    }
                    startOffset = typedRegion.getOffset() + typedRegion.getLength();
                    linenum = doc.getLineOfOffset(offset);
                    line = doc.getLineInformation(linenum);
                    typedRegion = getNSISPartitionAtOffset(doc, startOffset);
                }
                line = new Region(startOffset, line.getOffset() + line.getLength() - startOffset);
            } else {
                int linenum2 = linenum - 1;
                while (linenum2 >= 0) {
                    IRegion line2 = doc.getLineInformation(linenum2);
                    int endOffset = line2.getOffset() + line2.getLength() - 1;
                    if (endOffset >= 0) {
                        typedRegion = getNSISPartitionAtOffset(doc, endOffset);
                        if (!isValidRegionType(typedRegion.getType(), cValidPartitionTypes)) {
                            break;
                        }
                        if (doc.get(endOffset, 1).charAt(0) != LINE_CONTINUATION_CHAR) {
                            break;
                        }
                        typedRegion = getNSISPartitionAtOffset(doc, line2.getOffset());
                        if (!isValidRegionType(typedRegion.getType(), cValidPartitionTypes)) {
                            int startOffset = typedRegion.getOffset() + typedRegion.getLength();
                            line = new Region(startOffset, line.getOffset() + line.getLength() - startOffset);
                            break;
                        } else {
                            line = new Region(line2.getOffset(),
                                    line.getOffset() + line.getLength() - line2.getOffset());
                            linenum2--;
                        }
                    } else {
                        break;
                    }
                }
            }
            typedRegion = getNSISPartitionAtOffset(doc, line.getOffset() + line.getLength() - 1);
            if (!isValidRegionType(typedRegion.getType(), cValidPartitionTypes)) {
                if (contains(typedRegion, offset)) {
                    return new ITypedRegion[0][];
                }
                line = new Region(line.getOffset(), typedRegion.getOffset() - line.getOffset());
            } else {
                if (doc.get(line.getOffset() + line.getLength() - 1, 1).charAt(0) == LINE_CONTINUATION_CHAR) {
                    int linenum2 = linenum + 1;
                    int numlines = doc.getNumberOfLines();
                    while (linenum2 < numlines) {
                        IRegion line2 = doc.getLineInformation(linenum2);
                        typedRegion = getNSISPartitionAtOffset(doc, line2.getOffset());
                        if (!isValidRegionType(typedRegion.getType(), cValidPartitionTypes)) {
                            break;
                        }
                        int endOffset = line2.getOffset() + line2.getLength() - 1;
                        typedRegion = getNSISPartitionAtOffset(doc, endOffset);
                        if (!isValidRegionType(typedRegion.getType(), cValidPartitionTypes)) {
                            line = new Region(line.getOffset(), typedRegion.getOffset() - line.getOffset());
                            break;
                        } else {
                            line = new Region(line.getOffset(), endOffset - line2.getOffset() + 1);
                            if (doc.get(endOffset, 1).charAt(0) != LINE_CONTINUATION_CHAR) {
                                break;
                            } else {
                                linenum2++;
                            }
                        }
                    }
                }
            }
            ITypedRegion[] partitions = getNSISPartitions(doc);
            if (!Common.isEmptyArray(partitions)) {
                int startIndex = findRegion(partitions, line.getOffset());
                if (startIndex >= 0) {
                    int endIndex = findRegion(partitions, line.getOffset() + line.getLength() - 1);
                    if (endIndex >= 0 && endIndex >= startIndex) {
                        typedRegions = new ITypedRegion[endIndex - startIndex + 1];
                        for (int i = startIndex; i < endIndex + 1; i++) {
                            typedRegions[i - startIndex] = partitions[i];
                        }
                        typedRegions[0] = new TypedRegion(line.getOffset(),
                                typedRegions[0].getOffset() + typedRegions[0].getLength() - line.getOffset(),
                                typedRegions[0].getType());
                        endIndex -= startIndex;
                        typedRegions[endIndex] = new TypedRegion(typedRegions[endIndex].getOffset(),
                                line.getOffset() + line.getLength() - typedRegions[endIndex].getOffset(),
                                typedRegions[endIndex].getType());
                    }
                }
            }
        } catch (BadLocationException e) {
        }
        return getNSISLines(doc, typedRegions);
    }

    public static ITypedRegion[][] getNSISLines(IDocument doc, ITypedRegion[] typedRegions) {
        List<ITypedRegion[]> regions = new ArrayList<ITypedRegion[]>();
        if (doc != null && doc.getLength() > 0) {
            try {
                if (!Common.isEmptyArray(typedRegions)) {
                    String[] delims = doc.getLegalLineDelimiters().clone();
                    for (int i = 0; i < delims.length; i++) {
                        delims[i] = "\\" + delims[i]; //$NON-NLS-1$
                    }

                    int firstLine = doc.getLineOfOffset(typedRegions[0].getOffset());
                    ITypedRegion lastRegion = typedRegions[typedRegions.length - 1];
                    int lastLine = doc.getLineOfOffset(lastRegion.getOffset() + lastRegion.getLength() - 1);
                    for (int i = firstLine, index = 0; index < typedRegions.length && i <= lastLine; i++) {
                        List<TypedRegion> lineRegions = new ArrayList<TypedRegion>();
                        IRegion line = doc.getLineInformation(i);
                        String lineDelim = doc.getLineDelimiter(i);
                        //                        int start = line.getOffset();
                        int start = Math.max(line.getOffset(), typedRegions[index].getOffset());
                        int end = line.getOffset() + line.getLength() - 1
                                + (lineDelim != null ? lineDelim.length() : 0);

                        int partitionEnd = typedRegions[index].getOffset() + typedRegions[index].getLength() - 1;
                        String type = typedRegions[index].getType();
                        boolean validPartition = type.equals(NSISPartitionScanner.NSIS_STRING)
                                || type.equals(IDocument.DEFAULT_CONTENT_TYPE);

                        while (end >= start) {
                            if (partitionEnd > end) {
                                if (validPartition) {
                                    String text = doc.get(start, (end - start + 1));
                                    boolean found = false;
                                    for (int j = 0; j < delims.length; j++) {
                                        if (text.endsWith(delims[j])) {
                                            found = true;
                                            break;
                                        }
                                    }
                                    if (found) {
                                        i++;
                                        line = doc.getLineInformation(i);
                                        lineDelim = doc.getLineDelimiter(i);
                                        end = line.getOffset() + line.getLength() - 1
                                                + (lineDelim != null ? lineDelim.length() : 0);
                                    } else {
                                        lineRegions.add(new TypedRegion(start, (end - start + 1), type));
                                        break;
                                    }
                                } else {
                                    break;
                                }
                            } else if (partitionEnd < end) {
                                if (validPartition) {
                                    lineRegions.add(new TypedRegion(start, (partitionEnd - start + 1), type));
                                }
                                start = partitionEnd + 1;
                                index++;
                                if (index < typedRegions.length) {
                                    partitionEnd = typedRegions[index].getOffset() + typedRegions[index].getLength()
                                            - 1;
                                    type = typedRegions[index].getType();
                                    validPartition = type.equals(NSISPartitionScanner.NSIS_STRING)
                                            || type.equals(IDocument.DEFAULT_CONTENT_TYPE);
                                } else {
                                    break;
                                }
                            } else {
                                if (validPartition) {
                                    lineRegions.add(new TypedRegion(start, (end - start + 1), type));
                                }
                                index++;
                                break;
                            }
                        }

                        if (lineRegions.size() > 0) {
                            regions.add(lineRegions.toArray(new ITypedRegion[0]));
                        }
                    }
                }
            } catch (BadLocationException e) {
            }
        }
        return regions.toArray(new ITypedRegion[0][]);
    }

    /**
     * @param doc
     * @return
     * @throws BadLocationException
     */
    public static ITypedRegion[] getNSISPartitions(IDocument doc) {
        ITypedRegion[] typedRegions;
        try {
            if (doc != null) {
                if (doc instanceof IDocumentExtension3) {
                    try {
                        typedRegions = ((IDocumentExtension3) doc).computePartitioning(
                                NSISPartitionScanner.NSIS_PARTITIONING, 0, doc.getLength(), false);
                    } catch (BadPartitioningException e) {
                        typedRegions = doc.computePartitioning(0, doc.getLength());
                    }
                } else {
                    typedRegions = doc.computePartitioning(0, doc.getLength());
                }
            } else {
                typedRegions = new ITypedRegion[0];
            }
        } catch (BadLocationException ex) {
            typedRegions = new ITypedRegion[0];
        }
        return typedRegions;
    }

    /**
     * @param regionType
     * @param partitionTypes
     * @return
     */
    private static boolean isValidRegionType(String regionType, String[] partitionTypes) {
        boolean found = false;
        for (int i = 0; i < partitionTypes.length; i++) {
            if (partitionTypes[i].equals(regionType)) {
                found = true;
                break;
            }
        }
        return found;
    }

    /**
     * @param offset
     * @param doc
     * @param typedRegion
     * @return
     * @throws BadLocationException
     */
    public static ITypedRegion getNSISPartitionAtOffset(IDocument doc, int offset) throws BadLocationException {
        ITypedRegion typedRegion;
        if (doc instanceof IDocumentExtension3) {
            try {
                typedRegion = ((IDocumentExtension3) doc).getPartition(NSISPartitionScanner.NSIS_PARTITIONING,
                        offset, false);
            } catch (BadPartitioningException e) {
                typedRegion = doc.getPartition(offset);
            }
        } else {
            typedRegion = doc.getPartition(offset);
        }
        return typedRegion;
    }

    public static boolean sequenceDetected(ICharacterScanner scanner, char[] sequence,
            boolean lineContinuationAllowed, boolean eofAllowed) {
        int c;
        int offset = ((NSISScanner) scanner).getOffset();
        for (int i = 1; i < sequence.length; i++) {
            c = scanner.read();
            if (c == ICharacterScanner.EOF && eofAllowed) {
                return true;
            }

            if (c == LINE_CONTINUATION_CHAR) {
                int c2 = scanner.read();
                if (delimitersDetected(scanner, c2)) {
                    if (lineContinuationAllowed) {
                        i--;
                        continue;
                    } else {
                        unread(scanner, ((NSISScanner) scanner).getOffset() - offset);
                        return false;
                    }
                } else {
                    scanner.unread();
                }
            }

            if (c != sequence[i]) {
                unread(scanner, ((NSISScanner) scanner).getOffset() - offset);
                return false;
            }
        }

        return true;
    }

    public static void unread(ICharacterScanner scanner, int count) {
        for (int i = 0; i < count; i++) {
            scanner.unread();
        }
    }

    public static boolean delimitersDetected(ICharacterScanner scanner, int c) {
        char[][] delimiters = scanner.getLegalLineDelimiters();
        for (int i = 0; i < delimiters.length; i++) {
            if (c == delimiters[i][0] && sequenceDetected(scanner, delimiters[i], false, true)) {
                return true;
            }
        }
        return false;
    }

    private static boolean stringEscapeSequencesDetected(ICharacterScanner scanner, int c,
            char[][] specialSequences, boolean lineContinuationAllowed) {
        for (int i = 0; i < specialSequences.length; i++) {
            if (c == specialSequences[i][0]
                    && sequenceDetected(scanner, specialSequences[i], lineContinuationAllowed, true)) {
                return true;
            }
        }
        return false;
    }

    public static boolean stringEscapeSequencesDetected(ICharacterScanner scanner, int c) {
        if (!(stringEscapeSequencesDetected(scanner, c, QUOTE_ESCAPE_SEQUENCES, true))) {
            stringEscapeSequencesDetected(scanner, c, WHITESPACE_ESCAPE_SEQUENCES, false);
        }

        return true;
    }

    public static String getRegionText(IDocument document, IRegion region) {
        String text = null;
        if (document != null && region != null && region.getLength() > 0) {
            NSISTextProcessorRule rule = new NSISTextProcessorRule();
            NSISRegionScanner scanner = new NSISRegionScanner(document, region);
            rule.setTextProcessor(new DefaultTextProcessor());
            IToken token = rule.evaluate(scanner);
            text = (String) token.getData();
        }
        return text;
    }

    public static int insertTabString(StringBuffer buffer, int offsetInLine, int tabWidth) {
        if (tabWidth == 0) {
            buffer.append('\t');
            return 1;
        } else {
            int remainder = offsetInLine % tabWidth;
            remainder = tabWidth - remainder;
            for (int i = 0; i < remainder; i++) {
                buffer.append(' ');
            }
            return remainder;
        }
    }

    public static String flattenSyntaxStylesMap(Map<String, NSISSyntaxStyle> map) {
        StringBuffer buf = new StringBuffer(""); //$NON-NLS-1$
        if (!Common.isEmptyMap(map)) {
            Iterator<String> iter = map.keySet().iterator();
            String key = iter.next();
            NSISSyntaxStyle style = map.get(key);
            buf.append(key).append('#').append(style.toString());
            while (iter.hasNext()) {
                key = iter.next();
                style = map.get(key);
                buf.append('\u00FF').append(key).append('#').append(style.toString());
            }
        }
        return buf.toString();
    }

    public static Map<String, NSISSyntaxStyle> parseSyntaxStylesMap(String text) {
        Map<String, NSISSyntaxStyle> map = new LinkedHashMap<String, NSISSyntaxStyle>();
        String[] pairs = Common.tokenize(text, '\u00FF');
        if (!Common.isEmptyArray(pairs)) {
            for (int i = 0; i < pairs.length; i++) {
                String[] keyValue = Common.tokenize(pairs[i], '#');
                if (!Common.isEmptyArray(keyValue)) {
                    String key = keyValue[0];
                    if (keyValue.length > 1) {
                        try {
                            NSISSyntaxStyle style = NSISSyntaxStyle.parse(keyValue[1]);
                            map.put(key, style);
                            continue;
                        } catch (Exception ex) {
                            EclipseNSISPlugin.getDefault().log(ex);
                        }
                    }
                    map.put(key, null);
                }
            }
        }
        return map;
    }

    public static void hookSourceViewer(final ISourceViewer viewer) {
        final StyledText textWidget = viewer.getTextWidget();

        final FontRegistry fontRegistry = JFaceResources.getFontRegistry();
        textWidget.setFont(fontRegistry.get(JFaceResources.TEXT_FONT));
        final IPropertyChangeListener fontListener = new IPropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent event) {
                if (event.getProperty().equals(JFaceResources.TEXT_FONT)) {
                    textWidget.setFont(fontRegistry.get(JFaceResources.TEXT_FONT));
                }
            }
        };
        fontRegistry.addListener(fontListener);

        final Display display = textWidget.getDisplay();
        final HashMap<String, Color> map = new HashMap<String, Color>();
        final IPreferenceStore store = EditorsUI.getPreferenceStore();

        textWidget.setBackground(createColor(map, store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND,
                AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT, display));
        textWidget.setForeground(createColor(map, store, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND,
                AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT, display));

        final IPropertyChangeListener colorListener = new IPropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent event) {
                if (event.getProperty().equals(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND) || event
                        .getProperty().equals(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)) {
                    textWidget.setBackground(createColor(map, store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND,
                            AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT, display));
                } else if (event.getProperty().equals(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND) || event
                        .getProperty().equals(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)) {
                    textWidget.setForeground(createColor(map, store, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND,
                            AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT, display));
                }
            }
        };
        store.addPropertyChangeListener(colorListener);
        textWidget.addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                fontRegistry.removeListener(fontListener);
                store.removePropertyChangeListener(colorListener);
                for (Iterator<Color> iter = map.values().iterator(); iter.hasNext();) {
                    Color color = iter.next();
                    if (color != null && !color.isDisposed()) {
                        color.dispose();
                    }
                }
            }
        });
    }

    private static Color createColor(Map<String, Color> map, IPreferenceStore store, String key, String defaultKey,
            Display display) {
        if (!store.getBoolean(defaultKey)) {
            if (store.contains(key)) {
                RGB rgb = null;
                if (store.isDefault(key)) {
                    rgb = PreferenceConverter.getDefaultColor(store, key);
                } else {
                    rgb = PreferenceConverter.getColor(store, key);
                }
                Color color = new Color(display, rgb);
                Color oldColor = map.put(key, color);
                if (oldColor != null) {
                    oldColor.dispose();
                }
                return color;
            }
        }

        return null;
    }
}