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

package org.netbeans.modules.java;

import java.util.*;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.datatransfer.Transferable;
import javax.jmi.reflect.InvalidObjectException;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.swing.Timer;
import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
import java.beans.PropertyEditorSupport;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyEditor;
import java.lang.ref.WeakReference;
import javax.jmi.reflect.RefPackage;
import javax.swing.SwingUtilities;
import org.netbeans.jmi.javamodel.Resource;
import org.netbeans.modules.java.parser.JavaParser;
import org.netbeans.modules.javacore.JMManager;
import org.openide.filesystems.FileStateInvalidException;

import org.openide.util.*;
//import org.openide.src.*;
import org.openide.loaders.DataNode;
import org.openide.nodes.*;
import org.openide.filesystems.FileObject;
import org.openide.ErrorManager;
import org.openide.util.datatransfer.NewType;

import org.netbeans.modules.java.tools.BadgeCache;
import org.netbeans.modules.java.settings.JavaSettings;
import org.netbeans.modules.java.ui.nodes.elements.SourceChildren;
import org.netbeans.modules.java.ui.nodes.elements.SourceEditSupport;
import org.netbeans.modules.java.ui.nodes.SourceNodes;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.mdr.MDRepository;
import org.netbeans.api.mdr.events.ExtentEvent;
import org.netbeans.api.mdr.events.MDRChangeEvent;
import org.netbeans.api.mdr.events.MDRChangeListener;
import org.netbeans.api.mdr.events.MDRChangeSource;
import org.netbeans.api.queries.FileBuiltQuery;
import org.netbeans.jmi.javamodel.JavaClass;
import org.netbeans.jmi.javamodel.Method;
import org.netbeans.jmi.javamodel.Parameter;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.netbeans.modules.javacore.internalapi.ParsingListener;
import org.netbeans.modules.javacore.jmiimpl.javamodel.ResourceImpl;
  

/** The node representation of JavaDataObject for Java sources.
*
* @author Petr Hamernik
*/
public class JavaNode extends DataNode {
    private static final boolean DONT_RESOLVE_JAVA_BADGES = Boolean.getBoolean("perf.dont.resolve.java.badges");

    private static final String SHEETNAME_TEXT_PROPERTIES = "textProperties"; // NOI18N
    private static final String PROP_ENCODING = "encoding"; // NOI18N

    /** generated Serialized Version UID */
    static final long serialVersionUID = -7396485743899766258L;

    private static final String ICON_BASE = "org/netbeans/modules/java/resources/"; // NOI18N
    private static final String BARE_ICON_BASE = "org/netbeans/modules/java/resources/class"; // NOI18N

    private static final String BADGE_MAIN = ICON_BASE + "executable-badge"; // NOI18N
    private static final String BADGE_ERROR = ICON_BASE + "error-badge"; // NOI18N
    private static final String BADGE_NEEDS_COMPILE = ICON_BASE + "needs-compile"; // NOI18N
    
    private static final String[] ICON_ATTRS_NONE = {};
    
    protected static final String ICON_ATTR_NEEDS_COMPILE = "needsCompile"; // NOI18N
    /**
     * Icon attribute -- main class.
     */
    protected static final String   ICON_ATTR_MAIN = "mainClass"; // NOI18N
    
    /**
     * Icon attribute -- errors in the source.
     */
    protected static final String   ICON_ATTR_ERROR = "error"; // NOI18N

    private transient boolean preparsed = false;

    private static final int ICON_REFRESH_DELAY_MSECS = 1000;

    private BadgeCache  iconCache;
    
    private HashSet     currentBadges;
    
    private Task        iconRefreshTask;
    
    private IconResolver iconResolver;

    /**
     * Up-to-date status that will be used for badging.
     */
    private FileBuiltQuery.Status upToDate;    
    
    /**
     * True, if th parser was already initialized (and listener attached to it).
     */
    private boolean parserInitialized;

    /** Create a node for the Java data object using the default children.
    * @param jdo the data object to represent
    */
    public JavaNode (JavaDataObject jdo) {
	this(jdo, new JavaSourceChildren(jdo));
        currentBadges = new HashSet();
    }
    
    private void registerParserListener() {
	if (!parserInitialized) {
	    parserInitialized = true;
	    JavaMetamodel.addParsingListener(new JavaEditor.WParsingListener(mixedListener));
	}
    }     
        
    // T.B.: Workaround for issue #28623 - aggresively creating source element, classloading
    private static final class JavaSourceChildren extends SourceChildren {
        JavaDataObject jdo;
        private ExtentListener wExtentL;
        
        public JavaSourceChildren (JavaDataObject jdo) {
            super(SourceNodes.getExplorerFactory());
            this.jdo = jdo;
        }
	
        protected void addNotify() {
            Resource resource = JavaMetamodel.getManager().getResource(jdo.getPrimaryFile());
            setElement(resource);
            super.addNotify();
        }
	
        protected void removeNotify() {
            super.removeNotify();
            setElement (null);
        }
        
        public void setElement(final Resource element) {
            MDRepository repository = JavaMetamodel.getDefaultRepository();
            if (this.element == null && element != null) {
                if (wExtentL == null) {
                    wExtentL = new ExtentListener(this, (MDRChangeSource) repository);
                }
                ((MDRChangeSource) repository).addListener(wExtentL);
            } else if (element == null) {
                ((MDRChangeSource) repository).removeListener(wExtentL);
            }
            super.setElement(element);
        }
        
        // innerclass ...........................................................
        
        /** The listener for listening on extent unmount */
        private static final class ExtentListener extends WeakReference implements MDRChangeListener, Runnable {

            private final MDRChangeSource source;

            public ExtentListener(JavaSourceChildren referent, MDRChangeSource source) {
                super(referent, Utilities.activeReferenceQueue());
                this.source = source;
            }

            public void change(MDRChangeEvent e) {
                JavaSourceChildren sc = (JavaSourceChildren) get();
                if (sc == null) return;
                // if (sc.element == null || !sc.element.isValid()) return;
                if ((e instanceof ExtentEvent) && ((ExtentEvent) e).getType() == ExtentEvent.EVENT_EXTENT_DELETE) {
                    sc.setElement(JavaMetamodel.getManager().getResource(sc.jdo.getPrimaryFile()));
                }
            }

            public void run() {
                source.removeListener(this);
            }

        }
        
    } // JavaSourceChildren ................

    /**
     * This method is called during node initialization and whenever the base icon is
     * replaced to refill the badge cache with custom badges. The JavaNode's implementation
     * adds executable and error badges. Override to add your own badges, or replace the
     * default ones.
     */
    protected void initializeBadges(BadgeCache cache) {
        cache.registerBadge(ICON_ATTR_MAIN, BADGE_MAIN, 10, 5);
        cache.registerBadge(ICON_ATTR_ERROR, BADGE_ERROR, 8, 8);
        cache.registerBadge(ICON_ATTR_NEEDS_COMPILE, BADGE_NEEDS_COMPILE, 16, 0);
    }
    
    /** Create a node for the Java data object with configurable children.
    * Subclasses should use this constructor if they wish to provide special display for the child nodes.
    * Typically this would involve creating a subclass of {@link SourceChildren} based
    * on the {@link JavaDataObject#getSource provided source element}; the children list
    * may have extra nodes {@link Children#add(Node[]) added}, either at the {@link Children.Keys#setBefore beginning or end}.
    * @param jdo the data object to represent
    * @param children the children for this node
    */
    public JavaNode (JavaDataObject jdo, Children children) {
        super (jdo, children);
        initialize();
    }
    
    public void setIconBase(String base) {
        super.setIconBase(base);
        synchronized (this) {
            iconCache = BadgeCache.getCache(base);
            initializeBadges(iconCache);
        }
    }
    
    private class PropL implements ChangeListener,ParsingListener {
        public void stateChanged(ChangeEvent ev) {
            if (ev.getSource() instanceof FileBuiltQuery.Status) {
                requestCompileStatus();
            }
        }
        
        public void resourceParsed(Resource rsc) {
            FileObject fo = getDataObject().getPrimaryFile();
            try {
                if (fo.getPath().endsWith(rsc.getName()))
                    if (fo.equals(JavaMetamodel.getManager().getFileObject(rsc)))
                        requestResolveIcons();
            } catch (InvalidObjectException ioe) {
                //do nothing
                //resource is no more valid
            }
        }
    }
    
    private void updateCompiledStatus(boolean indicationEnabled) {
        if (indicationEnabled)
            requestCompileStatus();
        else {
            StateUpdater.getInstance().updateNode(this);
        }
    }

    private PropL mixedListener;
    
    private void initialize () {
        setIconBase(getBareIconBase());
        mixedListener = new PropL();
        StateUpdater.getInstance().registerNode(this);
        requestResolveIcons();
        registerParserListener();
    }

    private void requestCompileStatus() {
        if (!JavaSettings.getDefault().isCompileStatusEnabled()) {
            return;
        }
        synchronized (this) {
            if (iconRefreshTask != null) {
                iconResolver.setDetectCompile();
                return;
            }
            iconResolver = new IconResolver(true);
            iconRefreshTask = RequestProcessor.postRequest(iconResolver,
                ICON_REFRESH_DELAY_MSECS);
        }
    }
    
    private class IconResolver implements Runnable {
        boolean     detectCompile;
        
        IconResolver(boolean compile) {
            detectCompile = compile;
        }
        
        public void setDetectCompile() {
            detectCompile = true;
        }
        
        public void run() {
            synchronized (JavaNode.this) {
                iconRefreshTask = null;
                iconResolver = null;
            }
            if (detectCompile && upToDate == null) {
                upToDate = FileBuiltQuery.getStatus(JavaNode.this.getJavaDataObject().getPrimaryFile());
                if (upToDate != null) {
                    upToDate.addChangeListener(WeakListeners.change(mixedListener, upToDate));
                }
            }
            StateUpdater.getInstance().updateNode(JavaNode.this);
        }
    }
    
    /**
     * Schedules a refresh of node's icon, after ICON_REFRESH_DELAY_MSEC.
     * Requests is not scheduled if another one is still pending.
     * @deprecated
     */
    protected void requestResolveIcons() {
        if (upToDate == null)
           requestCompileStatus(); 
        synchronized (this) {
            if (iconRefreshTask != null)
                return;
            iconResolver = new IconResolver(false);
            iconRefreshTask = RequestProcessor.postRequest(iconResolver, 
                ICON_REFRESH_DELAY_MSECS);
        }
    }

    private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
        is.defaultReadObject();
        initialize();
    }
    
    static final void wrapThrowable(Throwable outer, Throwable inner, String message) {
        ErrorManager.getDefault().annotate(
            outer, ErrorManager.USER, null, message, inner, null);
    }

    /** Create the property sheet.
    * Subclasses may want to override this and add additional properties.
    * @return the sheet
    */
    protected Sheet createSheet () {
        Sheet sheet = super.createSheet();

        Sheet.Set ps = sheet.get(Sheet.PROPERTIES);
        ps.put(new PropertySupport.ReadWrite (
                   JavaDataObject.PROP_SYNCHRONIZATION_TYPE,
                   Byte.class,
                   Util.getString("PROP_synchMode"), // NOI18N
                   Util.getString("HINT_synchMode") // NOI18N
               ) {
                   public Object getValue() {
                       return new Byte(getJavaDataObject().getSynchronizationType());
                   }
                   public void setValue (Object val) throws InvocationTargetException {
                       if (val instanceof Byte) {
                           try {
                               getJavaDataObject().setSynchronizationType(((Byte) val).byteValue());
                               return;
                           }
                           catch(IllegalArgumentException e) {
                           }
                       }
                       throw new IllegalArgumentException();
                   }
                   public PropertyEditor getPropertyEditor() {
                       return new SyncModeEditor();
                   }
               });

        ps = new Sheet.Set();
        ps.setName(SHEETNAME_TEXT_PROPERTIES);
        ps.setDisplayName(Util.getString("PROP_textfileSetName")); // NOI18N
        ps.setShortDescription(Util.getString("HINT_textfileSetName")); // NOI18N
        ps.put(new PropertySupport.ReadWrite(PROP_ENCODING, 
            String.class, Util.getString("PROP_fileEncoding"), Util.getString("HINT_fileEncoding")) { // NOI18N
            public Object getValue() {
                String enc = Util.getFileEncoding0(getDataObject().getPrimaryFile());
                if (enc == null)
                    return "";
                else
                    return enc;
            }
            
            public void setValue(Object enc) throws InvocationTargetException {
                String encoding = (String)enc;
                if (encoding != null) {
                    if (!"".equals(encoding)) {
                        try {
                            sun.io.CharToByteConverter.getConverter(encoding);
                        } catch (IOException ex) {
                            InvocationTargetException t =  new InvocationTargetException(ex);
                            wrapThrowable(t, ex,
                                java.text.MessageFormat.format(Util.getString("FMT_UnsupportedEncoding"), // NOI18N
                                    new Object[] {
                                        encoding
                                    }
                                ));
                            throw t;
                        }
                    } else
                        encoding = null;
                }
                try {
                    Util.setFileEncoding(getDataObject().getPrimaryFile(), encoding);
                    ((JavaDataObject)getDataObject()).firePropertyChange0(PROP_ENCODING, null, null);
                } catch (IOException ex) {
                    throw new InvocationTargetException(ex);
                }
            }
        });
        sheet.put(ps);
        return sheet;
    }

    /** Get the associated Java data object.
    * ({@link #getDataObject} is protected; this provides access to it.)
    * @return the data object
    */
    protected JavaDataObject getJavaDataObject() {
        return (JavaDataObject) getDataObject();
    }
    
    public Image getIcon(int type) {
        return iconCache.getIcon(super.getIcon(type), getBadges());
    }
    
    public Image getOpenedIcon(int type) {
        return iconCache.getIcon(super.getOpenedIcon(type), getBadges());
    }

    /** Get the icon base.
    * This should be a resource path, e.g. /some/path/,
    * where icons are held. Subclasses may override this.
    * @return the icon base
    * @see #getIcons
    * @deprecated the function is not consistent with openide's setIconBase() behaviour.
    * Given the icon badging, there's no need for composing icon names - see
    * {@link #setBadges}, {@link #getBadges} or {@link #getBareIconBase()}
    */
    protected String getIconBase() {
        return ICON_BASE;
    }
    
    /**
     * Returns the base resource name for the basic icon that identifies object type
     * to the user. Additional images may be superimposed over that icon using icon
     * badging specification. Override in subclasses to provide a different icon for
     * the node.
     * @return the icon base 
     */
    protected String getBareIconBase() {
        return getIconBase() + getIcons()[0];
    }

    /** Get the icons.
    * This should be a list of bare icon names (i.e. no extension or path) in the icon base.
    * It should contain five icons in order for:
    * 
    *
  • a regular class *
  • a class with a main method *
  • a class with a parse error *
  • a JavaBean class *
  • a JavaBean class with a main method *
* Subclasses may override this. * @return the icons * @see #getIconBase * @deprecated State icons are now handled using icon badging mechanism. If you need * to add or modify the badges attached by the java module, override {@link #getBadges} */ protected String[] getIcons() { return new String[] { "class" }; // NOI18N } /** * Returns badges that should be combined with the base icon. * @return array of badges, can be null if no badges should be used. */ protected String[] getBadges() { if (currentBadges.isEmpty()) return null; return (String[])currentBadges.toArray(new String[currentBadges.size()]); } /** * Replaces the set of badges applied to the base icon. If the new set differs * from the current one, the new value is recorded and icon property changes are * fired. * @param badges list of badge identifiers; null to clear. */ protected void setBadges(String[] badges) { if (badges == null) badges = new String[0]; if (currentBadges.size() == badges.length) { boolean match = true; for (int i = 0; i < badges.length; i++) { if (!currentBadges.contains(badges[i])) { match = false; break; } } if (match) return; } currentBadges = new HashSet(Arrays.asList(badges)); fireIconChange(); fireOpenedIconChange(); } /** Update the icon for this node based on parse status. * Called automatically at the proper times. * @see #getIconBase * @see #getBadges * @see #setBadges */ protected void resolveIcons() { if (DONT_RESOLVE_JAVA_BADGES) return ; JavaDataObject jdo = getJavaDataObject(); FileObject fo = jdo.getPrimaryFile(); boolean isTemplate; try { isTemplate = fo.getFileSystem().isDefault() && jdo.isTemplate(); } catch (FileStateInvalidException fse) { isTemplate = false; } //TDB - avoid classloading by turning off parsing for templates, bug 28623 if (isTemplate) return; //Ignore virtual files #46348 if (fo.isVirtual()) return; final java.util.Collection badges = new java.util.ArrayList(3); final String desc; Resource resource = ((JMManager) JMManager.getManager()).getResource(jdo.getPrimaryFile(), false); if (resource != null) { // if (!resource.getErrors().isEmpty()) { // desc = Util.getString("HINT_ParsingErrors"); // badges.add(ICON_ATTR_ERROR); FileObject parent = fo.getParent(); ClassPath cp = ClassPath.getClassPath(parent, ClassPath.SOURCE); String pack = (cp == null) ? null : cp.getResourceName(parent, '.',false); // NOI18N // check the package String id = resource.getPackageName(); String pack2 = (id == null) ? "" : id; // NOI18N if (pack == null || !pack.equals(pack2)) { desc = new MessageFormat(Util.getString("FMT_Bad_Package")).format(new Object[] { pack2 }); //badges.add(ICON_ATTR_ERROR); } else { desc = null; } if (hasMain()) { badges.add(ICON_ATTR_MAIN); } } else { if (currentBadges.contains(ICON_ATTR_MAIN)) { badges.add(ICON_ATTR_MAIN); } desc=getShortDescription(); } // next: check whether the dataobject is up-to-date (if the settings permit that). // don't check it if the node corresponds to a template. if (!isTemplate) { String compBadge = resolveCompileBadge(); if (compBadge != null) badges.add(compBadge); } SwingUtilities.invokeLater(new Runnable() { public void run() { setShortDescription(desc); if (badges.isEmpty()) { setBadges(ICON_ATTRS_NONE); } else { setBadges((String[])badges.toArray(new String[badges.size()])); } } }); } private boolean hasMain() { JavaMetamodel.getDefaultRepository().beginTrans(false); try { JavaDataObject jdo = getJavaDataObject(); ResourceImpl r = (ResourceImpl) JavaMetamodel.getManager().getResource(jdo.getPrimaryFile()); if (r!=null && r.containsIdentifier("main")) { //NOI18N for (Iterator i = r.getClassifiers().iterator(); i.hasNext();) { JavaClass clazz = (JavaClass) i.next(); // now it is only important top-level class with the same // name as file. Continue if the file name differs // from top level class name. if (!clazz.getSimpleName().equals(jdo.getName())) continue; for (Iterator j = clazz.getFeatures().iterator(); j.hasNext();) { Object o = j.next(); // if it is not a method, continue with next feature if (!(o instanceof Method)) continue; Method m = (Method) o; int correctMods = (Modifier.PUBLIC | Modifier.STATIC); // check that method is named 'main' and has set public // and static modifiers! Method has to also return // void type. if (!"main".equals(m.getName()) || // NOI18N ((m.getModifiers() & correctMods) != correctMods) || (!"void".equals(m.getType().getName()))) continue; // check parameters - it has to be one of type String[] // or String... if (m.getParameters().size()==1) { Parameter par = ((Parameter) m.getParameters().get(0)); String typeName = par.getType().getName(); if (par.isVarArg() && ("java.lang.String".equals(typeName) || "String".equals(typeName))) { // NOI18N // Main methods written with variable arguments parameter: // public static main(String... args) { // } return true; } else if (typeName.equals("String[]") || typeName.equals("java.lang.String[]")) { // NOI18N // Main method written with array parameter: // public static main(String[] args) { // } return true; } } // end if parameters } // end features cycle } } } finally { JavaMetamodel.getDefaultRepository().endTrans(); } return false; } protected String resolveCompileBadge() { if (!JavaSettings.getDefault().isCompileStatusEnabled()) { return null; } return (upToDate != null && !upToDate.isBuilt()) ? ICON_ATTR_NEEDS_COMPILE : null; } public NewType[] getNewTypes() { if (getJavaDataObject().isJavaFileReadOnly()) { return super.getNewTypes(); } return SourceEditSupport.createJavaNodeNewTypes(this); } /** Simple property editor for two-item pulldown. */ static class SyncModeEditor extends PropertyEditorSupport { private static final String[] tags; static { tags = new String[3]; tags[JavaDataObject.CONNECT_NOT] = Util.getString("CTL_CONNECT_NOT"); tags[JavaDataObject.CONNECT_CONFIRM] = Util.getString("CTL_CONNECT_CONFIRM"); tags[JavaDataObject.CONNECT_AUTO] = Util.getString("CTL_CONNECT_AUTO"); } public String[] getTags () { return tags; } public String getAsText () { return tags[((Byte) getValue ()).byteValue ()]; } public void setAsText (String text) throws IllegalArgumentException { for (byte i = 0; i < tags.length; i++) { if (tags[i].equals (text)) { setValue (new Byte(i)); return; } } throw new IllegalArgumentException (); } } private static class StateUpdater implements PropertyChangeListener, ActionListener { private Set registeredNodes; private static StateUpdater instance; private Collection scheduledNodes; private Timer timer; StateUpdater() { JavaSettings.getDefault().addPropertyChangeListener(this); scheduledNodes = new LinkedList(); timer = new Timer(300, this); timer.setRepeats(false); } static synchronized StateUpdater getInstance() { if (instance == null) instance = new StateUpdater(); return instance; } public void registerNode(Node n) { if (registeredNodes == null) { synchronized (this) { if (registeredNodes == null) registeredNodes = new org.openide.util.WeakSet(37); } } synchronized (registeredNodes) { registeredNodes.add(n); } } public void updateNode(JavaNode n) { synchronized (this) { scheduledNodes.add(new java.lang.ref.WeakReference(n)); if (scheduledNodes.size() == 1) { timer.start(); } } timer.restart(); } public void propertyChange(PropertyChangeEvent evt) { if (!JavaSettings.PROP_SHOW_COMPILE_STATUS.equals(evt.getPropertyName())) // not interested. return; if (registeredNodes == null) return; Collection v; synchronized (registeredNodes) { v = new ArrayList(registeredNodes); } boolean enableStatus = ((Boolean)evt.getNewValue()).booleanValue(); for (Iterator it = v.iterator(); it.hasNext(); ) { ((JavaNode)it.next()).updateCompiledStatus(enableStatus); } } public void actionPerformed(ActionEvent actionEvent) { final Collection n; synchronized (this) { n = scheduledNodes; scheduledNodes = new LinkedList(); } RequestProcessor.getDefault().post(new Runnable() { public void run() { for (Iterator it = n.iterator(); it.hasNext(); ) { JavaNode node = (JavaNode)((java.lang.ref.Reference)it.next()).get(); if (node == null) continue; node.resolveIcons(); node.getDataObject().getCookie(JavaParser.class); } } }); } } }
... 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.