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-2002 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
package org.netbeans.modules.xml.tree.nodes;

import java.beans.*;
import java.util.*;
import java.awt.Component;

import org.openide.nodes.*;
import org.openide.util.actions.SystemAction;
import org.openide.util.Lookup;

import org.netbeans.tax.*;
import org.netbeans.modules.xml.tree.lib.GuiUtil;
import org.netbeans.modules.xml.tree.cookies.XMLNormalizeElementCookie;
import org.netbeans.modules.xml.tree.actions.XMLNormalizeElementAction;
import org.netbeans.modules.xml.tree.children.SubObjectListChildren;

import org.netbeans.modules.xml.tree.completion.GrammarManager;
import org.netbeans.modules.xml.tree.completion.TreeHintContext;
import org.netbeans.modules.xml.api.model.HintContext;
import org.netbeans.modules.xml.api.model.GrammarQuery;
import org.netbeans.modules.xml.core.XMLDataObject;
import org.openide.loaders.DataNode;
import org.openide.loaders.DataObject;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;

/**
 *
 * @author  Libor Kramolis
 * @version 0.1
 */
public class ElementNode extends AbstractParentNode {

    /**
     * Fired when the customizer for this node changes
     * (e.g. the element name changes to something with a
     * different type of customizer)
     */
    public static final String PROP_CUSTOMIZER = "customizer"; // NOI18N

    /** */
    private static final String NODE_TYPE = "#element"; // NOI18N

    /** */
    private static final int INDENT_STEP = 4; //??? -- it is only pilsen hack

    private static final String XML_ELEMENT_ICON = "/org/netbeans/modules/xml/tree/resources/elementNode.gif"; // NOI18N

    /** The element's customizer */
    protected ElementCustomizer m_customizer;

    /**
     * A template to apply to the text accompanying this ode.
     * The pattern is specified in the doctype map.
     */
    protected ElementTextFormatter m_nodeTextFormatter;

    /**
     * A flag used to instantiate the node's customizer
     * as lazily as possible.
     */
    private boolean m_bInitializedCustomizer = false;


    //
    // init
    //

    /** */
    public ElementNode (TreeElement treeElement) throws IntrospectionException {
        super (treeElement, new SubObjectListChildren (treeElement.getChildNodes(), treeElement.getAttributes(), null), "elementNode"); // NOI18N

        init();
    }

    public Component getCustomizer()
    { 
        if( !m_bInitializedCustomizer )
        { 
            // we haven't loaded the customizer
            // implementation yet...
            refreshGUI(true);
        } 

        if( m_customizer == null )
        { 
            return super.getCustomizer();
        } 
        else
        { 
            return m_customizer;
        }
    } 

    protected ElementTextFormatter getElementTextFormatter()
    { 
        if( m_nodeTextFormatter == null )
        { 
            m_nodeTextFormatter = new ElementTextFormatter();
        } 
        return m_nodeTextFormatter;
    } 

    /**
     * Make this property writable, so it can be set
     * from a properties file.
     */
    public void setCustomizer( ElementCustomizer c )
    { 
        if( m_customizer != c )
        { 
            Object oldCustomizer = m_customizer;
            m_customizer = c;
            if( m_customizer != null )
            { 
                m_customizer.setObject(this);
            } 

            firePropertyChange( PROP_CUSTOMIZER, oldCustomizer, c );
        } 
    } 

    /**
     * Bean property for the list of possible attributes
     * on this element (if a DTD or schema is available).
     * If no syntax completion is available, this will be 
     * an empty List.
     *
     * @return A List of org.w3c.dom.Attr objects
     */
    public List getAllowableAttributes()
    { 
        XMLDataObject dataObject = (XMLDataObject)getCookie(DataObject.class);
        if (dataObject == null) return Collections.EMPTY_LIST;
        
        GrammarQuery query = GrammarManager.getGrammar(dataObject);
        HintContext ctx = TreeHintContext.getContext( getElement(), "" );
        Enumeration en = query.queryAttributes(ctx);

        List result = new LinkedList();
        while( en.hasMoreElements() )
        { 
            result.add(en.nextElement());
        } 

        return result;
    } 


    /**
     */
    private void init () {
        CookieSet set = getCookieSet();
        CookieSet.Factory factory = new CookieFactory();

        set.add (XMLNormalizeElementCookie.class, factory);
//          set.add (GenerateDTDSupport.class, factory);
        
        refreshGUI(false);

        getElement().addPropertyChangeListener(new ElementNode.Listener());
    }

    /**
     * @param bCustomizer if true, call setCustomizer (for startup
     * and PROP_CUSTOMIZER events, but avoid the overhead otherwise)
     */
    protected void refreshGUI(boolean bCustomizer)
    { 
        // choose icon and customizer according to "doctype namespace"
        String sDocType = getDocumentType(getElement().getOwnerDocument());
        String uriType = getElement().getNamespaceURI();

        String sTagName = createName();
        getElementTextFormatter().setTagName(sTagName);

        //??? could not it be perfomance problem, I guess
        // that it should be ported to naming or cached
        
        Lookup.Result lookupResult = Lookup.getDefault().lookup(
                new Lookup.Template( DoctypeElementMap.class ) );

        Iterator iter = lookupResult.allInstances().iterator();

        while( iter.hasNext() )
        { 
            DoctypeElementMap dtc = (DoctypeElementMap)iter.next();
            
            if(sDocType != null && sDocType.equals(dtc.getDoctype())
            || uriType != null && uriType.equals(dtc.getNamespaceURI()))
            { 
                ElementInfo info = dtc.getElementInfo(sTagName);
                if( info != null )
                { 
                    if( info.getIconBase() == null )
                    { 
                        // use the standard 'element' icon
                        setIconBase(XML_ELEMENT_ICON);
                    } 
                    else
                    { 
                        // use the icon from the map file
                        // for this doctype
                        setIconBase(info.getIconBase());
                    } 

                    // if this is null, the getCustomizer
                    // accessor will defer to the standard
                    // element customizer
                    if( bCustomizer )
                    { 
                        if( m_bInitializedCustomizer )
                        { 
                            setCustomizer(info.getCustomizer());
                        } 
                        else
                        { 
                            // if we're just initializing now, we
                            // want to avoid firing a PROP_CUSTOMIZER
                            // event
                            m_bInitializedCustomizer = true;
                            m_customizer = info.getCustomizer();
                            if( m_customizer != null )
                            { 
                                m_customizer.setObject(this);
                            } 
                        } 
                    } 

                    if( info.getText() == null )
                    { 
                        getElementTextFormatter().setPattern(null);
                    } 
                    else
                    { 
                        getElementTextFormatter().setPattern(info.getText());
                    } 
                } 
                return;
            } 
        } 
    } 


    //
    // itself
    //

    /**
     */
    public final TreeElement getElement () {
        return (TreeElement) getTreeObject();
    }


    /**
     */
    protected boolean canAddTreeObject (TreeObject newObject) {
        if ( newObject instanceof TreeAttribute ) {
            return true;
        } else if ( newObject instanceof TreeNamedObjectMap ) {
            return true;
        } else {
            return super.canAddTreeObject (newObject);
        }
    }

    /**
     */
    protected boolean addTreeObject (TreeObject newObject) {
        if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("\n=- ElementNode::addTreeObject: newObject = " + newObject); // NOI18N

        if ( newObject instanceof TreeAttribute ) {
            return addAttribute ((TreeAttribute)newObject);

        } else if ( newObject instanceof TreeNamedObjectMap ) {
            return addAttributeList ((TreeNamedObjectMap)newObject);

        } else if ( newObject instanceof TreeCharacterData ) {
            return super.addTreeObject (newObject);

        } else {
            try {
                
                TreeChild lastChild = getElement().getLastChild();
                TreeObjectList objectList = getElement().getChildNodes();
                TreeText indentText = null;
                int indent = findIndent (getElement());
                
                // indent before add
                if ( lastChild instanceof TreeCharacterData ) {
                    if ( ( lastChild instanceof TreeText ) &&
                         ((TreeText)lastChild).onlyWhiteSpaces() ) {
                        indent ((TreeText)lastChild, indent + INDENT_STEP);
                    }
                } else {
                    indentText = new TreeText ("\n" + createIndent (indent + INDENT_STEP)); // NOI18N
                    objectList.add (indentText);
                }
                
                objectList.add (newObject);
                
                // indent after add
                indentText = new TreeText ("\n" + createIndent (indent)); // NOI18N
                objectList.add (indentText);

                return true;
            } catch (TreeException exc) {
                GuiUtil.notifyWarning (exc.getMessage());
                return false;
            }
        }
    }

    /**
     */
    private void indent (TreeText text, int indent) throws TreeException {
        if ( text == null ) {
            return;
        }

        String data = text.getData();
        StringBuffer dataSB = new StringBuffer (data);
        int lnl = data.lastIndexOf ('\n');
        if ( lnl != -1 ) {
            indent -= (data.length() - lnl - 1);
        } else {
            dataSB.append ('\n');
        }
        dataSB.append (createIndent (indent));
        text.setData (dataSB.toString());
    }


    /**
     */
    private int findIndent (TreeChild child) {
        int indent = 0;
        TreeParentNode parent = child.getParentNode();
        while ( parent instanceof TreeElement ) {
            indent += INDENT_STEP;
            parent = parent.getParentNode();
        }
        return indent;
    }

    /**
     */
    private String createIndent (int indent) {
        StringBuffer sb = new StringBuffer ();
        for (int i = 0; i < indent; i++) {
            sb.append (' ');
        }
        return sb.toString();
    }


    /**
     */
    protected boolean addAttributeList (TreeNamedObjectMap attributeList) {
        if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("\n=+ ElementNode::addAttributeList: attributeList = " + attributeList); // NOI18N

        boolean allAdded = true;

        Iterator it = attributeList.iterator();
        while (it.hasNext()) {
            Object next = it.next();
            if ( next instanceof TreeAttribute ) {
                allAdded &= addAttribute ((TreeAttribute)next);
            } else {
                allAdded = false;
            }
        }

        return allAdded;
    }

    /**
     */
    protected boolean addAttribute (TreeAttribute attribute) {
        if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("\n=# ElementNode::addAttribute: attribute = " + attribute); // NOI18N

	try {
	    boolean toSet = true;
	    TreeAttribute oldAttribute = getElement().getAttribute (attribute.getQName());
	    if ( oldAttribute != null ) {
		toSet = GuiUtil.confirmAction (Util.THIS.getString ("MSG_replace_attribute", attribute.getQName()));
	    }
	    if ( toSet ) {
		getElement().addAttribute (attribute);
                return true;
	    }
	} catch (TreeException exc) {
	    GuiUtil.notifyTreeException (exc);
	}
        return false;
    }


    //
    // Order
    //

    /**
     */
    protected boolean canChangeOrder () {
        return true;
    }


    //
    // from AbstractObjectNode
    //

    /** Name of property used as node name. Used to listen on it.
     */
    protected final String getPresentableNamePropertyName () {
        return TreeElement.PROP_TAG_NAME;
    }

    /**
     */
    protected final void setPresentableNameProperty (String name) throws TreeException {
        getElement().setQName (name);
    }

    /**
     */
    public String getDisplayName()
    { 
        List attributes = getAllowableAttributes();

        if( attributes.size() != 0 )
        { 
            String[] attValues = new String[attributes.size()];
            Iterator iter = attributes.iterator();
            int i=0;
            while( iter.hasNext() )
            { 
                Attr domAttr = (Attr)iter.next();
                TreeAttribute attr = getElement().getAttribute(domAttr.getName());
                if( attr != null )
                { 
                    attValues[i++] = attr.getValue();
                } 
                else
                { 
                    attValues[i++] = ""; //NOI8N
                } 
            } 
            return getElementTextFormatter().format( attributes, attValues );
        } 

        // if the above didn't work out (for whatever reason)
        // just use the back-off strategy (the name of the element)
        return super.getDisplayName();
    } 

    /**
     */
    protected final String createName () {
        return getElement().getQName();
    }


    /**
     */
    protected final String createNodePreview () {
        StringBuffer sb = new StringBuffer();

        sb.append ("<").append (getElement().getQName()).append (" ... />"); // NOI18N
        
        return sb.toString();
    }


    /**
     */
    protected final String getNodeTypePrefix () {
        return NODE_TYPE;
    }


    /**
     */
    protected final SystemAction[] createNodeSpecificActions () {
        return new SystemAction[] {
//             SystemAction.get (XMLGenerateAction.GenerateDTDAction.class),
            SystemAction.get (XMLNormalizeElementAction.class),
        };
    }


    //
    // NewTypes
    //

    /**
     */
    protected Object[] getNewTypesTypes () {
        XMLDataObject dataObject = (XMLDataObject)getCookie(DataObject.class);
        if (dataObject == null) return new Object[0];
        GrammarQuery query = GrammarManager.getGrammar(dataObject);

        HintContext ctx = TreeHintContext.getContext( getElement(), "" );
        Enumeration en = query.queryElements(ctx);

        List elementList = new LinkedList();
        while( en.hasMoreElements() )
        { 
            elementList.add(en.nextElement());
        }

        // TODO: query for allowable text children

        return elementList.toArray();
    }

    /**
     * Try to get the doctype from the DTD declaration, or the string
     * representing the root namespace for a document using schemas.
     */
    private static String getDocumentType( TreeDocumentRoot root )
    { 
        if( root instanceof TreeDocument )
        {
            // try the DTD first
            TreeDocumentType docType = ((TreeDocument)root).getDocumentType();
            if( docType != null )
            {
                return docType.getPublicId();
            }

            // try the schema's default namespace
            TreeElement rootElement = ((TreeDocument)root).getDocumentElement();
            TreeAttribute defaultNS = rootElement.getAttribute("xmlns"); // NOI18N
            if( defaultNS != null )
            { 
                return defaultNS.getValue();
            } 
        }

        return "";
    } 


    //
    // XMLNormalizeElementCookie
    //

    /**
     *
     */
    private class InnerCookieImpl implements XMLNormalizeElementCookie {

        /**
         */
        public final void normalize () {
            try {
                GuiUtil.setStatusText (Util.THIS.getString ("MSG_normalize_start"));
                ElementNode.this.getElement().normalize();
                GuiUtil.setStatusText (Util.THIS.getString ("MSG_normalizeFinished"));
            } catch (TreeException exc) {
                GuiUtil.setStatusText (Util.THIS.getString ("MSG_normalize_error"));
                GuiUtil.notifyTreeException (exc);
            }
        }
        
    } // end: class InnerCookieImpl


    //
    // class CookieFactory
    //

    /**
     *
     */
    private class CookieFactory implements CookieSet.Factory {

        /** Creates new Cookie */
        public Node.Cookie createCookie (Class clazz) {
            if ( clazz == XMLNormalizeElementCookie.class ) {
                return new InnerCookieImpl();
//              } else if ( clazz == GenerateDTDSupport.class ) {
//                  return new GenerateDTDSupport (ElementNode.this, ElementNode.this.getElement());
            }

            return null;
        }
        
    }

    private class Listener implements PropertyChangeListener
    { 
        public void propertyChange( PropertyChangeEvent evt )
        { 
            if( m_bChangeInProgress )
            { 
                return;
            } 

            if( evt.getSource() != ElementNode.this.getElement() ||
                evt.getPropertyName() == null )
            { 
                return;
            } 

            if( evt.getPropertyName().equals(TreeElement.PROP_TAG_NAME) ||
                evt.getPropertyName().equals(TreeElement.PROP_ATTRIBUTES) )
            { 
                try
                { 
                    m_bChangeInProgress = true;
                    ElementNode.this.refreshGUI(
                        evt.getPropertyName().equals(
                            TreeElement.PROP_TAG_NAME) );
                } 
                finally
                { 
                    m_bChangeInProgress = false;
                } 
            } 
        } 

        private boolean m_bChangeInProgress = false;
    } 

}
... 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.