alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 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
    }

}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.