All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.editor.GuardedDocument Maven / Gradle / Ivy

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.editor;

import java.beans.PropertyChangeEvent;
import java.lang.ref.WeakReference;
import java.text.MessageFormat;
import java.util.Hashtable;
import java.util.Enumeration;
import java.awt.Color;
import java.awt.Font;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.StyledDocument;
import javax.swing.text.Style;
import javax.swing.text.Element;
import javax.swing.text.AttributeSet;
import javax.swing.text.StyleContext;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.CannotRedoException;
/**
* Extension to the guarded document that implements
* StyledDocument interface
*
* @author Miloslav Metelka
* @version 1.00
*/

public class GuardedDocument extends BaseDocument
    implements StyledDocument {

    /** Guarded attribute used for specifying that the inserted block
    * will be guarded.
    */
    public static final String GUARDED_ATTRIBUTE = "guarded"; // NOI18N

    /** AttributeSet with only guarded attribute */
    public static final SimpleAttributeSet guardedSet = new SimpleAttributeSet();

    /** AttributeSet with only break-guarded attribute */
    public static final SimpleAttributeSet unguardedSet = new SimpleAttributeSet();

    private static final boolean debugAtomic = Boolean.getBoolean("netbeans.debug.editor.atomic"); // NOI18N
    private static final boolean debugAtomicStack = Boolean.getBoolean("netbeans.debug.editor.atomic.stack"); // NOI18N

    // Add the attributes to sets
    static {
        guardedSet.addAttribute(GUARDED_ATTRIBUTE, Boolean.TRUE);
        unguardedSet.addAttribute(GUARDED_ATTRIBUTE, Boolean.FALSE);
    }

    public static final String FMT_GUARDED_INSERT_LOCALE = "FMT_guarded_insert"; // NOI18N
    public static final String FMT_GUARDED_REMOVE_LOCALE = "FMT_guarded_remove"; // NOI18N

    MarkBlockChain guardedBlockChain;

    /** Break the guarded flag, so inserts/removals over guarded areas will work */
    boolean breakGuarded;

    boolean atomicAsUser;

    /** Style context to hold the styles */
    protected StyleContext styles;

    /** Style to layer name mapping */
    protected Hashtable stylesToLayers;

    /** Name of the normal style. The normal style is used to reset the effect
    * of all styles applied to the line.
    */
    protected String normalStyleName;

    public GuardedDocument(Class kitClass) {
        this(kitClass, true, new StyleContext());
    }

    /** Create base document with a specified syntax and style context.
    * @param kitClass class used to initialize this document with proper settings
    *   category based on the editor kit for which this document is created
    * @param syntax syntax scanner to use with this document
    * @param styles style context to use
    */
    public GuardedDocument(Class kitClass, boolean addToRegistry, StyleContext styles) {
        super(kitClass, addToRegistry);
        this.styles = styles;
        stylesToLayers = new Hashtable(5);
        addLayer(new DrawLayerFactory.GuardedLayer(), DrawLayerFactory.GUARDED_LAYER_VISIBILITY);
        guardedBlockChain = new MarkBlockChain.LayerChain(this, DrawLayerFactory.GUARDED_LAYER_NAME);
    }

    /** Get the chain of the guarded blocks */
    public MarkBlockChain getGuardedBlockChain() {
        return guardedBlockChain;
    }

    public boolean isPosGuarded(int pos) {
        int rel = guardedBlockChain.compareBlock(pos, pos) & MarkBlock.IGNORE_EMPTY;
        return (rel == MarkBlock.INSIDE_BEGIN || rel == MarkBlock.INNER);
    }

    /** This method is called automatically before the document
    * is updated as result of removal. This function can throw
    * BadLocationException or its descendants to stop the ongoing
    * insert from being actually done.
    * @param evt document event containing the change including array
    *  of characters that will be inserted
    */
    protected void preInsertCheck(int offset, String text, AttributeSet a)
    throws BadLocationException {
        super.preInsertCheck(offset, text, a);

        int rel = guardedBlockChain.compareBlock(offset, offset) & MarkBlock.IGNORE_EMPTY;

        if (debugAtomic) {
            System.err.println("GuardedDocument.beforeInsertUpdate() atomicAsUser=" // NOI18N
                               + atomicAsUser + ", breakGuarded=" + breakGuarded // NOI18N
                               + ", inserting text='" + EditorDebug.debugString(text) // NOI18N
                               + "' at offset=" + Utilities.debugPosition(this, offset)); // NOI18N
            if (debugAtomicStack) {
                Thread.dumpStack();
            }
        }

        if (text.length() > 0
                && (rel & MarkBlock.OVERLAP) != 0
                && rel != MarkBlock.INSIDE_END // guarded blocks have insertAfter endMark
                && !(text.charAt(text.length() - 1) == '\n'
                     && rel == MarkBlock.INSIDE_BEGIN)
           ) {
            if (!breakGuarded || atomicAsUser) {
                throw new GuardedException(
                    MessageFormat.format(
                        LocaleSupport.getString(FMT_GUARDED_INSERT_LOCALE),
                        new Object [] {
                            new Integer(offset)
                        }
                    ),
                    offset
                );
            }
        }
    }

    /** This method is called automatically before the document
    * is updated as result of removal.
    */
    protected void preRemoveCheck(int offset, int len)
    throws BadLocationException {
        int rel = guardedBlockChain.compareBlock(offset, offset + len);

        if (debugAtomic) {
            System.err.println("GuardedDocument.beforeRemoveUpdate() atomicAsUser=" // NOI18N
                               + atomicAsUser + ", breakGuarded=" + breakGuarded // NOI18N
                               + ", removing text='" + EditorDebug.debugChars(getChars(offset, len)) // NOI18N
                               + "'at offset=" + Utilities.debugPosition(this, offset)); // NOI18N
            if (debugAtomicStack) {
                Thread.dumpStack();
            }
        }

        if ((rel & MarkBlock.OVERLAP) != 0
                || (rel == MarkBlock.CONTINUE_BEGIN
                    && !(offset == 0 || getChars(offset - 1, 1)[0] == '\n'))
           ) {
            if (!breakGuarded || atomicAsUser) {
                // test whether the previous char before removed text is '\n'
                throw new GuardedException(
                    MessageFormat.format(
                        LocaleSupport.getString(FMT_GUARDED_REMOVE_LOCALE),
                        new Object [] {
                            new Integer(offset)
                        }
                    ),
                    offset
                );
            }
        }
    }

    public void setCharacterAttributes(int offset, int length, AttributeSet s,
                                       boolean replace) {
        if (((Boolean)s.getAttribute(GUARDED_ATTRIBUTE)).booleanValue() == true) {
            guardedBlockChain.addBlock(offset, offset + length, false); // no concat
            fireChangedUpdate(createDocumentEvent(offset, length, DocumentEvent.EventType.CHANGE));
        }
        if (((Boolean)s.getAttribute(GUARDED_ATTRIBUTE)).booleanValue() == false) {
            guardedBlockChain.removeBlock(offset, offset + length);
            fireChangedUpdate(createDocumentEvent(offset, length, DocumentEvent.EventType.CHANGE));
        }
    }

    public void runAtomic(Runnable r) {
        if (debugAtomic) {
            System.out.println("GuardedDocument.runAtomic() called"); // NOI18N
            if (debugAtomicStack) {
                Thread.dumpStack();
            }
        }

        boolean origBreakGuarded = breakGuarded;
        try {
            breakGuarded = true;
            super.runAtomicAsUser(r);
        } finally {
            breakGuarded = origBreakGuarded;
            if (debugAtomic) {
                System.out.println("GuardedDocument.runAtomic() finished"); // NOI18N
            }
        }
    }

    public void runAtomicAsUser(Runnable r) {
        if (debugAtomic) {
            System.out.println("GuardedDocument.runAtomicAsUser() called"); // NOI18N
            if (debugAtomicStack) {
                Thread.dumpStack();
            }
        }

        boolean origAtomicAsUser = atomicAsUser;
        try {
            atomicAsUser = true;
            super.runAtomicAsUser(r);
        } finally {
            if (debugAtomic) {
                System.out.println("GuardedDocument.runAtomicAsUser() finished"); // NOI18N
            }
            atomicAsUser = origAtomicAsUser;
        }
    }

    protected BaseDocumentEvent createDocumentEvent(int offset, int length,
            DocumentEvent.EventType type) {
        return new GuardedDocumentEvent(this, offset, length, type);
    }

    /** Adds style to the document */
    public Style addStyle(String styleName, Style parent) {
        String layerName = (String)stylesToLayers.get(styleName);
        if (layerName == null) {
            layerName = styleName; // same layer name as style name
            addStyleToLayerMapping(styleName, layerName);
        }

        Style style =  styles.addStyle(styleName, parent);
        if (findLayer(layerName) == null) { // not created by default
            try {
                extWriteLock();
                addStyledLayer(layerName, style);
            } finally {
                extWriteUnlock();
            }
        }
        return style;
    }

    public void addStyleToLayerMapping(String styleName, String layerName) {
        stylesToLayers.put(styleName, layerName);
    }

    /** Removes style from document */
    public void removeStyle(String styleName) {
        styles.removeStyle(styleName);
    }

    /** Fetches style previously added */
    public Style getStyle(String styleName) {
        return styles.getStyle(styleName);
    }

    /** Set the name for normal style. Normal style is used to reset the effect
    * of all aplied styles.
    */
    public void setNormalStyleName(String normalStyleName) {
        this.normalStyleName = normalStyleName;
    }

    /** Fetches the list of style names */
    public Enumeration getStyleNames() {
        return styles.getStyleNames();
    }

    /** Change attributes for part of the text.  */
    public void setParagraphAttributes(int offset, int length, AttributeSet s,
                                       boolean replace) {
        // !!! implement
    }

    /**
     * Sets the logical style to use for the paragraph at the
     * given position.  If attributes aren't explicitly set
     * for character and paragraph attributes they will resolve
     * through the logical style assigned to the paragraph, which
     * in turn may resolve through some hierarchy completely
     * independent of the element hierarchy in the document.
     *
     * @param pos the starting position >= 0
     * @param s the style to set
     */
    public void setLogicalStyle(int pos, Style s) {
        try {
            extWriteLock();
            pos = Utilities.getRowStart(this, pos);
            String layerName = (String)stylesToLayers.get(s.getName());
            // remove all applied styles
            DrawLayer[] layerArray = getDrawLayerList().currentLayers();
            for (int i = 0; i < layerArray.length; i++) {
                if (layerArray[i] instanceof DrawLayerFactory.StyleLayer) {
                    ((DrawLayerFactory.StyleLayer)layerArray[i]).markChain.removeMark(pos);
                }
            }
            // now set the requested style
            DrawLayerFactory.StyleLayer styleLayer
            = (DrawLayerFactory.StyleLayer)findLayer(layerName);
            if (styleLayer != null) {
                styleLayer.markChain.addMark(pos);
            }
            fireChangedUpdate(createDocumentEvent(pos, 0,
                                                  DocumentEvent.EventType.CHANGE)); // enough to say length 0
        } catch (BadLocationException e) {
            // do nothing for invalid positions
        } finally {
            extWriteUnlock();
        }
    }

    /** Get logical style for position in paragraph */
    public Style getLogicalStyle(int pos) {
        try {
            pos = Utilities.getRowStart(this, pos);
            DrawLayer[] layerArray = getDrawLayerList().currentLayers();
            for (int i = 0; i < layerArray.length; i++) {
                DrawLayer layer = layerArray[i];
                if (layer instanceof DrawLayerFactory.StyleLayer) {
                    if (((DrawLayerFactory.StyleLayer)layer).markChain.isMark(pos)) {
                        return ((DrawLayerFactory.StyleLayer)layer).style;
                    }
                }
            }
            return getStyle(normalStyleName); // no style found
        } catch (BadLocationException e) {
            return null;
        }
    }

    /**
     * Gets the element that represents the character that
     * is at the given offset within the document.
     *
     * @param pos the offset >= 0
     * @return the element
     */
    public Element getCharacterElement(int pos) {
        return getParagraphElement(pos);
    }


    /**
     * Takes a set of attributes and turn it into a foreground color
     * specification.  This might be used to specify things
     * like brighter, more hue, etc.
     *
     * @param attr the set of attributes
     * @return the color
     */
    public Color getForeground(AttributeSet attr) {
        return null; // !!!
    }

    /**
     * Takes a set of attributes and turn it into a background color
     * specification.  This might be used to specify things
     * like brighter, more hue, etc.
     *
     * @param attr the set of attributes
     * @return the color
     */
    public Color getBackground(AttributeSet attr) {
        return null; // !!!
    }

    /**
     * Takes a set of attributes and turn it into a font
     * specification.  This can be used to turn things like
     * family, style, size, etc into a font that is available
     * on the system the document is currently being used on.
     *
     * @param attr the set of attributes
     * @return the font
     */
    public Font getFont(AttributeSet attr) {
        return new Font("Default",Font.BOLD,12); // NOI18N
    }

    protected DrawLayer addStyledLayer(String layerName, Style style) {
        if (layerName != null) {
            try {
                int indColon = layerName.indexOf(':');
                int layerVisibility = Integer.parseInt(layerName.substring(indColon + 1));
                DrawLayer layer = new DrawLayerFactory.StyleLayer(layerName, this, style);

                addLayer(layer, layerVisibility);
                return layer;

            } catch (NumberFormatException e) {
                // wrong name, let it pass
            }
        }
        return null;
    }

    public String toStringDetail() {
        return super.toStringDetail()
               + getDrawLayerList()
               + ",\nGUARDED blocks:\n" + guardedBlockChain; // NOI18N
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy