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-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.java.ui.editors;

import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;

import java.util.Enumeration;
import java.util.StringTokenizer;

import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;

import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;

import org.openide.nodes.Node;
import org.openide.nodes.NodeOp;
import org.openide.explorer.ExplorerManager;


/**
 * This class is designed to be a liaison between an JTextComponent and 
 * an Explorer TreeView. The class can be used either as one way synchonizer
 * between any {@link Document} and {@link ExplorerManager}, so it will parse
 * the document and select or expand appropriate nodes using the manager, or
 * a bidirectional one. In the latter case, it needs a JTextComponent to operate
 * on and it will complete the document, if possible, from the nodes collection
 * and manage selection for the user to save typing.
 *
 * @author  sd99038
 * @version 
 */
public class TreePathWalker implements DocumentListener, PropertyChangeListener {
    /**
     * ExplorerManager used to navigate.
     */
    ExplorerManager     manager;
    
    /**
     * Current delimiter.
     */
    String              delimiter;
    
    /**
     * Text component to work with.
     */
    JTextComponent      textComponent;
    
    /**
     * Disable handling events from the explorer manager.
     */
    boolean             disableExplorer;
    
    /**
     * Default delimiter used to split input contents to a path.
     */
    public static final String  DEFAULT_DELIMITER = "."; // NOI18N
    
    /**
     * Constructs a navigator which uses a particular Explorer manager. Default
     * delimiter (dot) is used in this case.
     * @param em manager which will be used for navigation and context search
     */
    public TreePathWalker(ExplorerManager em) {
        this(em, DEFAULT_DELIMITER);
    }
    
    /**
     * Constructs the navigator with some explorer manager and string tokenizer 
     * used to split document contents into node names.
     * @param em manager which will be used for navigation and context search.
     * @param delimiter to use when parsing user input
     */
    public TreePathWalker(ExplorerManager man, String delimiter) {
       this.delimiter = delimiter;
       this.manager = man;
       
       man.addPropertyChangeListener(this);
    }

    /**
     * Attaches the navigation support to a text component so it can
     * update selection in it.
     */
    public void setTextComponent(JTextComponent text) {
        if (this.textComponent != null)
            textComponent.getDocument().removeDocumentListener(this);
        textComponent = text;
        if (text != null)
            text.getDocument().addDocumentListener(this);
    }
    
    /** 
     * Retrieves the associated text component.
     * @return text component, or null.
     */
    public JTextComponent getTextComponent() {
        return textComponent;
    }
    
    private void selectString(Document doc) {
        try {
            String s = doc.getText(0, doc.getLength());
            selectString(s);
        } catch (BadLocationException ex) {
        }
    }

    /**
     * Implementation method. Do not call or override.
     */
    public final void removeUpdate(javax.swing.event.DocumentEvent documentEvent) {
        //selectString(documentEvent.getDocument());
    }

    /**
     * Implementation method. Do not call or override.
     */
    public final void insertUpdate(javax.swing.event.DocumentEvent documentEvent) {
        selectString(documentEvent.getDocument());
    }
    
    /**
     * Implementation method. Do not call or override.
     */
    public final void changedUpdate(javax.swing.event.DocumentEvent documentEvent) {
        selectString(documentEvent.getDocument());
    }
    
    private boolean nameMatches(Node child, String component) {
        return child.getName().startsWith(component);
    }
    
    private boolean namesEqual(Node child, String component) {
        return child.getName().equals(component);
    }
    
    private Enumeration enumerateComponents(String s) {
        return new StringTokenizer(s, getDelimiter());
    }
    
    public String getDelimiter() {
        return this.delimiter;
    }
    
    public ExplorerManager getExplorerManager() {
        return this.manager;
    }

    /**
     * Method which actually selects (or attempts to) some node in the Manager.
     */
    private void selectString(String sel) {
        Enumeration components = enumerateComponents(sel);
        
        Node context = getExplorerManager().getRootContext();
        String component;
        Node found = null;
        boolean exact = false;
        String suffix = ""; // NOI18N
        //Collection candidates = new LinkedList();
        boolean ambiguous = false;
        
        System.err.println("begin search"); // NOI18N
        
        while (components.hasMoreElements()) {
            String comp = (String)components.nextElement();
            Enumeration children = context.getChildren().nodes();
            int compLen = comp.length();
            found = null;
            exact = false;
            //candidates.clear();
            ambiguous = false;
            
            System.err.println("got component: " + comp); // NOI18N
            suffix = null;
            
            while (children.hasMoreElements()) {
                Node ch = (Node)children.nextElement();
                String childName = ch.getName();

                // node name does not start with the prefix -> ignore.
                if (!nameMatches(ch, comp)) {
                    continue;
                }
                if (found != null)
                    ambiguous = true;
                if (!exact) {
                    found = ch;
                }
                if (namesEqual(ch, comp)) {
                    // exact match!
                    exact = true; // overrides ambiguous.
                    suffix = ""; // NOI18N
                } else {
                    String childSuffix = childName.substring(compLen);
                    if (suffix == null)
                        suffix = childSuffix;
                    else
                        suffix = commonPrefix(suffix, childSuffix);
                }
                
                // node matches, need to put it on the candidate list.
                //candidates.add(ch);
            }
            
            if (found == null || 
                (ambiguous && !exact)) {
                // match was not found, or was not exact - we cannot
                // proceed to further items in the selector anyway.
                break;
            }
            
            context = found;
        }
        // nothing was found -> do nothing.
        if (found == null)
            return;
        
        if (exact || !ambiguous) {
            // select the corresponding Node in the ExplorerManager:
            selectNode(found);
            // if the text really ends in a separator -> expand the node!
            if (sel.endsWith(getDelimiter())) {
                expandNode(found);
            }
        }
        
        JTextComponent textComp = getTextComponent();
        if ("".equals(suffix) || textComp == null)
            return;
        appendSelectedString(textComp, suffix);
    }

    private void expandNode(Node n) {
        getExplorerManager().setExploredContext(n);
    }
     
    
    private void selectNode(Node n) {
        try {
            disableExplorer = true;
            getExplorerManager().setSelectedNodes(new Node[] {
                n
            });
        } catch (PropertyVetoException ex) {
            // ignore the veto.
            // Advanced feature -- display reason of the veto in 
            // the text component's tooltip ??
        } finally {
            disableExplorer = false;
        }
    }
    
    private void appendSelectedString(final JTextComponent comp, final String suffix) {
        if ("".equals(suffix))
            return;
        
        final Document doc = comp.getDocument();
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    Caret c = comp.getCaret();
                    int docLength = doc.getLength();
                    doc.insertString(docLength, suffix, null);

                    int newLength = doc.getLength();
                    c.setDot(newLength);
                    c.moveDot(docLength);
                } catch (BadLocationException ex) {
                    ex.printStackTrace();
                }
            }
        });
    }
    
    /**
     * Returns a common prefix of the two passed strings.
     */
    private String commonPrefix(String first, String second) {
        int l1 = first.length();
        int l2 = second.length();
        int maxl = l1 > l2 ? l2 : l1;
        
        for (int i = 0; i < maxl; i++) {
            if (first.charAt(i) != second.charAt(i)) {
                return first.substring(0, i);
            }
        }
        return maxl == l1 ? first : second;
    }
    
    private void displayNodePath(Node target) {
        String[] path = NodeOp.createPath(target, getExplorerManager().getRootContext());
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < path.length; i++) {
            if (i > 0)
                sb.append(getDelimiter());
            sb.append(path[i]);
        }
        JTextComponent txt = getTextComponent();
        if (txt != null) {
            Document d = txt.getDocument();
            try {
                d.remove(0, d.getLength());
                d.insertString(0, sb.toString(), null);
            } catch (BadLocationException ex) {
            }
        }
    }
    
    public void propertyChange(java.beans.PropertyChangeEvent propertyChangeEvent) {
        if (disableExplorer)
            return;
        
        String evName = propertyChangeEvent.getPropertyName();
        if (ExplorerManager.PROP_SELECTED_NODES.equals(evName)) {
            // Need a policy how to reflect changes made in the explorer back
            // to the document or input line
            Node[] selNodes = getExplorerManager().getSelectedNodes();
            if (selNodes.length == 0)
                return;
            displayNodePath(selNodes[0]);
        }
    }
}
... 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.