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.bridge;

import java.beans.*;
import java.io.IOException;
import java.util.*;
import java.lang.ref.WeakReference;
import java.net.URL;

import org.openide.nodes.Node;
import org.openide.nodes.CookieSet;
import org.openide.src.*;

import org.netbeans.modules.java.ElementFactory;
import javax.jmi.reflect.RefBaseObject;
import javax.jmi.reflect.RefObject;
import javax.jmi.reflect.InvalidObjectException;
import org.netbeans.api.mdr.MDRepository;
import org.netbeans.api.mdr.events.*;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.classpath.GlobalPathRegistry;
import org.netbeans.jmi.javamodel.Array;
import org.netbeans.jmi.javamodel.Codebase;
import org.netbeans.jmi.javamodel.Feature;
import org.netbeans.jmi.javamodel.JavaClass;
import org.netbeans.jmi.javamodel.TypeReference;
import org.netbeans.jmi.javamodel.ArrayReference;
import org.netbeans.jmi.javamodel.ClassDefinition;
import org.netbeans.jmi.javamodel.JavaModelPackage;
import org.netbeans.jmi.javamodel.JavaPackage;
import org.netbeans.jmi.javamodel.MultipartId;
import org.netbeans.jmi.javamodel.NamedElement;
import org.netbeans.jmi.javamodel.ParameterizedType;
import org.netbeans.jmi.javamodel.PrimitiveType;
import org.netbeans.jmi.javamodel.PrimitiveTypeKind;
import org.netbeans.jmi.javamodel.PrimitiveTypeKindEnum;
import org.netbeans.jmi.javamodel.Resource;
import org.netbeans.jmi.javamodel.TypeParameter;
import org.netbeans.jmi.javamodel.UnresolvedClass;
import org.netbeans.mdr.handlers.InstanceHandler;
import org.netbeans.modules.java.JavaDataObject;
import org.netbeans.modules.javacore.ClassIndex;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.netbeans.modules.javacore.jmiimpl.javamodel.TypeClassImpl;
import org.netbeans.modules.javacore.internalapi.JMIElementCookie;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.openide.ErrorManager;
import org.openide.filesystems.FileObject;
import org.openide.text.EditorSupport;
import org.openide.util.Utilities;

/**
 *
 * @author Dan Prusa
 */
public abstract class ElementImpl implements Element.Impl, Element.Impl2, ElementProperties, Node.Cookie, ElementEvents {
        
    public static final String JAVA_LANG_OBJECT = "java.lang.Object"; // NOI18N
    
    /** The element for this class.
     */
    private transient Element               element;

    /**
     * Collection of registered PropertyChangeListeners.
     */
    private transient Collection	    propListeners;
        
    /** Vetoable change support.
     * PENDING: Need to create special VetoableChangeSupport, as the standard one
     * is not sufficient for the fine-grained events.
     */
    private transient VetoableChangeSupport vetoSupport;

    /**
     * Binding to the underlying source/whatever.
     */
    protected transient Binding               binding;
    
    /**
     * True, if the element has been already created and was not destroyed.
     */
    private transient boolean               valid;

    /**
     * Set of cookies for this element. The set conveniently contains only cookies for
     * the implementation class hierarchy.
     */
    private CookieSet                       cookieSet;

    /**
     * Model that has created this element and that is used to manage the element.
     * The element delegates management functions like locking and event queueing to
     * its model.
     */
    protected transient DefaultLangModel      model;

    /**
     * Flag that is used to signal that the element is being created. Some processing
     * is reduced to minimum during this mode and event firing is supressed.
     */
    private transient boolean               inCreation;
    
    protected JavaDocImpl                   javadoc;
    
    /**
     * Java element in repository corresponding to this element.
     */
    protected transient RefObject           javaElement;
    
    protected transient JavaModelPackage    javaModelPackage;
    
    protected transient MDRepository        repository;
    
    private static final Binding NULL_BINDING = new NullBinding();
    
    /**
     * Enables debug trace messages for event generation
     */
    private static final boolean DEBUG_EVENTS = false;

    private static final long serialVersionUID = -6337836874152020892L;
     
           
    protected ElementImpl(DefaultLangModel model) {
        this.model = model;
        this.javaElement = null;
        this.inCreation = false;
        this.valid = true;
        this.repository = JavaMetamodel.getDefaultRepository ();
        binding = NULL_BINDING;
    }
    
    protected ElementImpl(DefaultLangModel model, RefObject javaElement) {
        this.model = model;
        this.javaElement = javaElement;
        this.inCreation = false;
        this.valid = true;
        this.repository = JavaMetamodel.getDefaultRepository ();
        binding = NULL_BINDING;
        try {
            javaModelPackage = (JavaModelPackage) javaElement.refImmediatePackage ();            
        } catch (javax.jmi.reflect.InvalidObjectException e) {
        }
    }
    
    public RefObject getJavaElement () {
        return javaElement;
    }
    
    public Type descrToType (org.netbeans.jmi.javamodel.Type type) {
        return descrToType (TypeClassImpl.getRealType(type), null);
    }
    
    public Type descrToType (org.netbeans.jmi.javamodel.Type descr, String sourceName) {
        if (descr == null)
            return null;
        if (descr instanceof TypeParameter) {
            return Type.createClass (createClassIdentifier (JAVA_LANG_OBJECT));
        }
        if (descr instanceof ParameterizedType) {
            if (((ParameterizedType)descr).getParameters().size() == 0) {
                descr = ((ParameterizedType)descr).getDefinition();
            } else {
                if (sourceName == null)
                    return Type.createClass (createClassIdentifier (JAVA_LANG_OBJECT));
                else
                    return Type.createClass (createClassIdentifier (JAVA_LANG_OBJECT, sourceName));
            }
        }
        if (descr instanceof Array) {
            return Type.createArray (descrToType (((Array) descr).getType ()));
        } else if (descr instanceof JavaClass) {
            // [MaM] migration
            String name = ((JavaClass) descr).getName ();
//            if (name.equals ("String")) {
//                name = "java.lang.String";
//            }
            // System.out.println("descrToType, CLASS: " + name);
            if (sourceName == null)
                return Type.createClass (createClassIdentifier (name));
            else
                return Type.createClass (createClassIdentifier (name, sourceName));
        } else {
            // primitive type
            PrimitiveTypeKind kind = ((PrimitiveType) descr).getKind ();                            
            if (PrimitiveTypeKindEnum.BOOLEAN.equals (kind))
                return Type.BOOLEAN;
            if (PrimitiveTypeKindEnum.INT.equals (kind))
                return Type.INT;
            if (PrimitiveTypeKindEnum.CHAR.equals (kind))
                return Type.CHAR;
            if (PrimitiveTypeKindEnum.BYTE.equals (kind))
                return Type.BYTE;
            if (PrimitiveTypeKindEnum.SHORT.equals (kind))
                return Type.SHORT;
            if (PrimitiveTypeKindEnum.LONG.equals (kind))
                return Type.LONG;
            if (PrimitiveTypeKindEnum.FLOAT.equals (kind))
                return Type.FLOAT;
            if (PrimitiveTypeKindEnum.DOUBLE.equals (kind))
                return Type.DOUBLE;            
            return descr != null ? Type.VOID : null; // [PENDING] should null be returned ???
        }
    }
    
    // [MaM] this method should probably by changed to "resolveType"
    public org.netbeans.jmi.javamodel.Type typeToDescr (Type type) {
        if (type.isArray ()) {
            return javaModelPackage.getArray().resolveArray(typeToDescr(type.getElementType()));
        } else {            
            if (type.isClass()) {
                return getClassDescriptor(type.getClassName().getFullName());
            } else {
                // primitive type
                return (PrimitiveType) javaModelPackage.getType().resolve(type.getFullString());
                
                // [MaM] migration to the new infrastructure
//                PrimitiveTypeKind tag;
//                if (Type.BOOLEAN.equals (type))
//                    tag = PrimitiveTypeKindEnum.BOOLEAN;          
//                else if (Type.INT.equals (type))
//                    tag = PrimitiveTypeKindEnum.INT;
//                else if (Type.CHAR.equals (type))
//                    tag = PrimitiveTypeKindEnum.CHAR;
//                else if (Type.BYTE.equals (type))
//                    tag = PrimitiveTypeKindEnum.BYTE;
//                else if (Type.SHORT.equals (type))
//                    tag = PrimitiveTypeKindEnum.SHORT;
//                else if (Type.LONG.equals (type))
//                    tag = PrimitiveTypeKindEnum.LONG;
//                else if (Type.FLOAT.equals (type))
//                    tag = PrimitiveTypeKindEnum.FLOAT;
//                else if (Type.DOUBLE.equals (type))
//                    tag = PrimitiveTypeKindEnum.DOUBLE;
//                else
//                    tag = PrimitiveTypeKindEnum.VOID;
//                return codeBase.createPrimitive(tag);
            }
        }
    }
    
    public TypeReference typeToTypeReference(Type type) {
        if (type.isArray ()) {
            int dimCount = 0;
            do {
                dimCount++;
                type = type.getElementType();
            } while (type.isArray());
            String name = type.getSourceString();
            if (name == null)
                name = type.getFullString();
            MultipartId parent = javaModelPackage.getMultipartId().createMultipartId(name, null, null);
            return javaModelPackage.getArrayReference().createArrayReference("", parent, dimCount);
        } else {
            String name = type.getSourceString();
            if (name == null)
                name = type.getFullString();
            return javaModelPackage.getMultipartId().createMultipartId(name, null, null);
        }
    }
    
    public Type stringToType (String name) {
        return stringToType(name, name);
    }
    
    public Type stringToType (String fullName, String sourceName) {
        if ("boolean".equals(fullName)) // NOI18N
            return Type.BOOLEAN;
        if ("int".equals (fullName)) // NOI18N
            return Type.INT;
        if ("char".equals (fullName)) // NOI18N
            return Type.CHAR;
        if ("byte".equals (fullName)) // NOI18N
            return Type.BYTE;
        if ("short".equals (fullName)) // NOI18N
            return Type.SHORT;
        if ("long".equals (fullName)) // NOI18N
            return Type.LONG;
        if ("float".equals (fullName)) // NOI18N
            return Type.FLOAT;
        if ("double".equals (fullName)) // NOI18N
            return Type.DOUBLE;
        if ("void".equals (fullName) || fullName == null) // [PENDING] // NOI18N
            return Type.VOID;
        return Type.createClass (createClassIdentifier(fullName, sourceName));
    }
    
    public Type typeReferenceToType (TypeReference descr) {
        if (descr == null)
            return null;
        Type type = null;
        if (descr instanceof ArrayReference) {
            type = multipartIdToType(descr.getParent());
            int dimCount = ((ArrayReference) descr).getDimCount();
            for (int x = 0; x < dimCount; x++) {
                type = Type.createArray (type);
            }
        } else {
            type = multipartIdToType((MultipartId)descr);
        }
        return type;
    }
    
    private Type multipartIdToType(MultipartId id) {
        String sourceName = multipartIdToName(id);
        String fullName;
        NamedElement elem = id.getElement();
        int resolved = Identifier.RESOLVED;
        if (elem instanceof PrimitiveType)
            return stringToType(sourceName, null);            
        if (JAVA_LANG_OBJECT.equals(sourceName)) {
            fullName = sourceName;
        } else {
            if (elem != null && (elem instanceof JavaClass)) {
                fullName = ((JavaClass) elem).getName();
            } else {
                fullName = null;
            }
            
            // for some reason the fullname is sometimes null even if the
            // above condition is true (a class returns null from get name?)
            if (fullName == null) {
                fullName = sourceName;
                resolved = Identifier.UNRESOLVED;
            }
        }
        return Type.createClass(Identifier.create(fullName, sourceName, resolved));
    }
    
    // [MaM] - migration to the new infrastructure
    // this method should probably by changed to "resolveClass"
    public JavaClass getClassDescriptor (String fullName) {
        return (JavaClass) javaModelPackage.getType().resolve(fullName);
//        ClassDescriptor res = getCodebase ().findClassDescriptor(fullName, true);
//        return res;
    }

    public Identifier createClassIdentifier (MultipartId id) {
        String sourceName = multipartIdToName(id);
        String fullName = null;
        JavaClass jcls = (JavaClass)id.getElement();
        if (JAVA_LANG_OBJECT.equals(sourceName)) {
            fullName = sourceName;
        } else {
            fullName = jcls == null ? sourceName : typeToFullName(jcls);
        }
        int resolved = Identifier.RESOLVED;
        
        if (jcls == null || jcls instanceof UnresolvedClass)
            resolved = Identifier.UNRESOLVED;
        
        return Identifier.create(fullName, sourceName, resolved);
    }
    
    public static String multipartIdToName(MultipartId id) {
        LinkedList list = new LinkedList();
        while (id != null) {
            
            if (!id.getTypeArguments().isEmpty()) {
                return JAVA_LANG_OBJECT;
            }
            
            list.addFirst(id.getName());
            id = id.getParent();
        }
        StringBuffer buf = new StringBuffer();
        for (Iterator iter = list.iterator(); iter.hasNext();) {
            buf.append((String)iter.next());
            if (iter.hasNext())
                buf.append('.');
        }
        return buf.toString();
    }
    
    public String typeToFullName(JavaClass jc) {
        if (jc instanceof ParameterizedType) {
            ParameterizedType p = (ParameterizedType) jc;
            StringBuffer buf = new StringBuffer();
            buf.append(typeToFullName(p.getDefinition()));
            List pars = p.getParameters();
            if (pars.size() > 0) {
                buf.append('<');
                for (Iterator iter = pars.iterator(); iter.hasNext(); ) {
                    JavaClass par = (JavaClass) iter.next();
                    buf.append(typeToFullName(par));
                    if (iter.hasNext())
                        buf.append(',');
                }
                buf.append('>');
            }
            return buf.toString();
        } else {
            return jc.getName();
        }
    }
    
    public Identifier createClassIdentifier (String fullName) {
        return createClassIdentifier (fullName, fullName);
    }
    
    public Identifier createClassIdentifier (String fullName, String sourceName) {
//        ProjectModel pm = ProjectModel.getDefault ();
        
        // [MaM] migration to the new infrastructure
        repository.beginTrans(false);
        try {
            setClassPath();
            boolean exists = ClassIndex.hasClass(fullName, JavaMetamodel.getManager().getClassPath());
            //boolean exists = pm.getDefaultClassPath ().exists (fullName);
            return Identifier.create (
                fullName, sourceName,
                exists ? Identifier.RESOLVED : Identifier.UNRESOLVED
            );
        } finally {
            repository.endTrans(false);
        }
    }
    
    public void checkIsValid () throws SourceException {
        try {
            javaElement.refImmediatePackage ();
        } catch (javax.jmi.reflect.InvalidObjectException e) {
            throwIsInvalid ();
        }
    }
    
    public void throwIsInvalid () throws SourceException {        
        Util.throwException("Element was deleted", "EXC_ElementInvalid"); // NOI18N
    }
    
    public abstract void connectListener ();
    
    /**
     * Attaches the abstract layer to the element; since the all properties required
     * for the Binding to operate are available, the binding is created as well.
     */
    public void attachedToElement(Element el) {        
        this.element = el;
        if (!valid)
            return;
        repository.beginTrans (false);
        try {
            try {
                javaElement.refImmediateComposite();
            } catch (javax.jmi.reflect.InvalidObjectException e) {
                setValid(false);
            }
            connectListener ();
            setValid (true);
        } catch (javax.jmi.reflect.InvalidObjectException e) {
        } finally {
            repository.endTrans (false);
        }
    }
    
    /**
     * Binds the element to a particular underlying Binding. The function does nothing
     * if the binding was already establised; it is impossible to rebind an element once
     * the binding was established (this constraint may be overriden by descendants)
     *
     * @param b binding to use for element i/o operations.
     */
    public void setBinding(Binding b) {
        if (this.binding != null)
            return;
        if (b instanceof Node.Cookie) {
            getCookieSet().add((Node.Cookie)b);
        }
        binding = b;
    }
    
    // Listener interface functions
    ///////////////////////////////////////////////////////////////////////////////////
    
    private String describeEvent(PropertyChangeEvent evt) {
        StringBuffer sb = new StringBuffer();
        sb.append(evt.getPropertyName());
        Object oldV = evt.getOldValue();        
        sb.append(" old = " + describeValue(evt.getOldValue())); // NOI18N
        sb.append(" new = " + describeValue(evt.getNewValue())); // NOI18N
        return sb.toString();
    }
    
    private String describeValue(Object o) {
        if (o instanceof Identifier) {
            Identifier id = (Identifier)o;
            return id.getSourceName() + "/" + id.getFullName() + "/" +  // NOI18N
                id.getResolutionStatus();
        } else if (o instanceof Identifier[]) {
            StringBuffer sb = new StringBuffer();
            sb.append("[ "); // NOI18N
            Identifier[] ids = (Identifier[])o;
            for (int i = 0; i < ids.length; i++) {
                if (i > 0)
                    sb.append(", "); // NOI18N
                sb.append(describeValue(ids[i]));
            }
            sb.append(" ]"); // NOI18N
            return sb.toString();
        } else if (o instanceof MethodParameter[]) {
            MethodParameter[] pars = (MethodParameter[])o;
            if (pars.length == 0)
                return "()"; // NOI18N
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < pars.length; i++) {
                if (i > 0)
                    sb.append(", "); // NOI18N
                sb.append(pars[i].getSourceString());
                sb.append("/"); // NOI18N
                Type t = pars[i].getType();
                sb.append(t.getFullString());
                while (t.isArray())
                    t = t.getElementType();
                if (t.isClass()) {
                    sb.append('/');
                    sb.append(new Integer(t.getTypeIdentifier().getResolutionStatus()).toString());
                }
            }
            return sb.toString();
        } else if (o instanceof Type) {
            Type t = (Type)o;
            String s = t.getSourceString() + "/" + t.getFullString(); // NOI18N
            while (t.isArray())
                t = t.getElementType();
            if (t.isClass()) {
                s = s + "/" + t.getTypeIdentifier().getResolutionStatus(); // NOI18N
            }
            return s;
        } else if (o != null) {
            return o.toString();
        } else return "null"; // NOI18N
    }

    /** Adds property listener. 
     */
    public void addPropertyChangeListener (PropertyChangeListener l) {
        if (DEBUG_EVENTS) {
            System.err.println("[" + this + "] attaching listener " + l); // NOI18N
        }        
        if (propListeners == null) {
            synchronized (this) {
                // new test under synchronized block
                if (propListeners == null) {
                    propListeners = new LinkedList();
                    initializeListenerSupport();
                }
            }
        }
	synchronized (propListeners) {
	    propListeners.add(l);
	}
    }

    /**
     * Returns true, if the element is fully created. This yes/no test is used for supression
     * of some event firing and lightweight operations.
     */
    protected boolean isCreated() {
        return this.inCreation;
    }
    
    /** Removes property listener */
    public void removePropertyChangeListener (PropertyChangeListener l) {
        if (DEBUG_EVENTS) {
            System.err.println("[" + this + "] removing listener " + l); // NOI18N
        }
	if (propListeners != null) {
	    synchronized (propListeners) {
		propListeners.remove(l);
	    }
	}
    }

    /** Adds property vetoable listener */
    public void addVetoableChangeListener (VetoableChangeListener l) {
        if (DEBUG_EVENTS) {
            System.err.println("[" + this + "] attaching veto listener " + l); // NOI18N
        }
        if (vetoSupport == null) {
            synchronized (this) {
                // new test under synchronized block
                if (vetoSupport == null) {
                    vetoSupport = new VetoableChangeSupport (element);
                    initializeListenerSupport();
                }
            }
        }
        vetoSupport.addVetoableChangeListener (l);
    }
    
    /** Removes property vetoable listener */
    public void removeVetoableChangeListener (VetoableChangeListener l) {
        if (DEBUG_EVENTS) {
            System.err.println("[" + this + "] removing veto listener " + l); // NOI18N
        }
        if (vetoSupport != null) {
            vetoSupport.removeVetoableChangeListener (l);
        }
    }

    /** true if some vetoable listener is currently registered */
    public boolean hasVetoableListeners (String propName) {
        if (vetoSupport == null)
            return false;
        else
            return vetoSupport.hasListeners (propName);
    }
    
    /** Base method for marking the current insertion point.
     */
    public void markCurrent(boolean beforeAfter) {
        // PENDING: redirect to the new facilities for element ordering.
    }
    
    /** Returns the abstract wrapper for this implementation.
     */
    public final Element getElement() {
        return element;
    }
    
    /**
     * Returns the cookie set so that extension objects can plug in and
     * extend the element implementation.
     * @return r/w cookie set instance.
     */
    public final CookieSet getCookieSet() {
        if (cookieSet == null) {
            synchronized (this) {
                if (cookieSet == null) {
                    cookieSet = new CookieSet();
                    initializeCookies(cookieSet);
                }
            }
        }
        return cookieSet;
    }
    
    protected void initializeCookies(CookieSet set) {
        set.add(this);
        set.add(new JMIElementCookieImpl());
    }
    
    /**
     * Returns true, if the element is still valid - that is present in the model.
     * @return true, iff the element is still valid.
     */
    public boolean isValid() {
        return this.valid;
    }
    
    // Support functions 
    /////////////////////////////////////////////////////////////////////////////////
    
    /** Fires property change event. The event is fired as "own" property change event,
     * so it is captured in the queue and counted for the summary information.
    * @param name property name
    * @param o old value
    * @param n new value
    */
    protected final void firePropertyChange(String name, Object o, Object n) {
        fireOwnPropertyChange(new PropertyChangeEvent(getElement(), name, o, n));
    }
    
    /**
     * Fires a PropertyChangeEvent to the listeners, if there's a listener.
     */
    protected final void firePropertyChangeEvent(PropertyChangeEvent evt) {
        if (DEBUG_EVENTS) {
            StringBuffer sb = new StringBuffer();
            sb.append('[');
            sb.append(toString());
            sb.append("] Dispatching change: "); // NOI18N
            sb.append(describeEvent(evt));
            System.err.println(sb.toString());
        }
	if (propListeners == null)
	    return;
	
	Vector listeners;
	synchronized (propListeners) {
	    listeners = new Vector(propListeners);
	}
	for (int i = 0; i < listeners.size(); i++) {
	    PropertyChangeListener l = (PropertyChangeListener)listeners.elementAt(i);
	    l.propertyChange(evt);
	}
    }
    
    /**
     * Fires an arbitrary property change event about intrinsic property change.
     * Events fired though this method will show up in the change summary events
     * fired after the model's lock is released. The method does nothing if the element
     * is in the creation phase.

* Important note: since the implementation will record the current element's * state by creating a copy/clone, it is required that the change is fired * before the element updates its internal state so that the clone contains the * old one. */ protected final void fireOwnPropertyChange(PropertyChangeEvent evt) { if (evt==null) return; /* final int MAX = 20; String oldValue = evt.getOldValue () != null ? evt.getOldValue ().toString () : ""; if (oldValue.length () > MAX) oldValue = oldValue.substring (0, MAX); String newValue = evt.getNewValue () != null ? evt.getNewValue ().toString () : ""; if (newValue.length () > MAX) newValue = newValue.substring (0, MAX); */ // System.out.println("EVT: " + evt.getPropertyName() + " " + oldValue + " " + newValue + " " + this.hashCode()); firePropertyChangeEvent (evt); /* if (isCreated()) return; EventQueue q = getModelImpl().getEventQueue(); q.elementChanged(this); addPropertyChange(evt); */ } protected Element cloneSelf() { throw new UnsupportedOperationException("clone unsupported on " // NOI18N + getClass()); } /** * Adds an extrinsic property change event to the final event queue. * Events channeled through this method do not contribute to the summary events, * for example properties that contain children sub-elements should use * this method to avoid unnecessary element cloning. */ public final void addPropertyChange(PropertyChangeEvent evt) { if (isCreated()) return; getModelImpl().getEventQueue().addPropertyChange(this, evt); } /** Fires a vetoable change on property `name' from old value `o' to new value `n'. * @param name name of the property that is being changed. * @param o old value of the property; can be null. * @param n new value of the property; can be null. */ protected final void fireVetoableChange(String name, Object o, Object n) throws PropertyVetoException { if (isCreated()) return; if (vetoSupport != null) { try { getModelImpl().notifyEventsDispatched(true); vetoSupport.fireVetoableChange(name, o, n); } finally { getModelImpl().notifyEventsDispatched(false); } } } /** * Fires arbitrary pre-constructed vetoable change event. * @param evt vetoable event that should be fired. */ public final void fireVetoableChange(PropertyChangeEvent evt) throws SourceException { if (isCreated()) return; checkVetoablePropertyChange(evt); } private void doFireVetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { if (DEBUG_EVENTS) { StringBuffer sb = new StringBuffer(); sb.append('['); sb.append(toString()); sb.append("] Dispatching veto: "); // NOI18N sb.append(describeEvent(evt)); System.err.println(sb.toString()); } if (vetoSupport != null) { try { getModelImpl().notifyEventsDispatched(true); vetoSupport.fireVetoableChange(evt); } finally { getModelImpl().notifyEventsDispatched(false); } } } /** The method tries to fire a Vetoable Change event; if (one of) listener throws * PropertyVetoException, the exception is examined whether it holds a wrapped SourceException. * If so, that inner SourceException is rethrown, otherwise, the PropertyVetoException is * wrapped into a SourceException and thrown to the caller. */ protected void checkVetoablePropertyChange(PropertyChangeEvent evt) throws SourceException { if (isCreated() || !isConstrained()) return; try { doFireVetoableChange(evt); } catch (SourceVetoException ex) { // rethrow the original exception. throw ex.getNestedException(); } catch (PropertyVetoException ex) { // rethrow the veto as a general SourceException. // PENDING: use model's environment to annotate/log the exception(s). throw new SourceException(ex.getMessage()); } } /** * Returns the storage binding for this element. The element must be * backed up by a text file/document, but the changes themselves and the binding * to the underlying document are performed by other delegate object. * If the element is just being created, the function returns NULL_BINDING that accepts * all requests, but does nothing. This way the element is blocked from altering the * storage until it is fully created. * * @return The binding for this element, or, if the element has inCreation flag, * it returns the default NULL_BINDING. */ public final Binding getBinding() { if (isCreated()) return NULL_BINDING; return this.binding; } /** * Returns the actual binding without any shielding for elements that are not yet * created. */ public final Binding getRawBinding() { return getBinding (); } /** Invalidates the element. The element will be no longer valid and may refuse some * operations that would depend on other objects in the model - since it will be no * longer considered to be a part of a model. */ protected void invalidate() { setValid(false); } private void setValid(boolean valid) { boolean old = this.valid; if (old == valid) return; this.valid = valid; if (old) // do not clone the element upon THIS property change. fireOwnPropertyChange(new PropertyChangeEvent(getEventSource(), PROP_VALID, valid ? Boolean.FALSE : Boolean.TRUE, valid ? Boolean.TRUE : Boolean.FALSE )); } /** * Checks whether the element is still valid. If not, throws a SourceException * to indicate that the calling operation cannot be completed. * @throws SourceException if the element is not valid/present in the model. */ protected void checkValid(Object lockToken) throws SourceException { if (isValid()) { return; } releaseLock(lockToken); Util.throwException("Element was deleted", "EXC_ElementInvalid"); // NOI18N } public void checkDocument() { JavaDataObject jdo = model.getJavaDataObject(); EditorSupport edSupport = (EditorSupport) jdo.getCookie (EditorSupport.class); if (!edSupport.isDocumentLoaded()) { try { edSupport.openDocument(); } catch (IOException e) { ErrorManager.getDefault().notify(e); } } } public void setClassPath() { JavaDataObject jdo = model.getJavaDataObject(); FileObject fo = jdo.getPrimaryFile(); JavaMetamodel.getManager().setClassPath(fo); } /** * JDK 1.5 - checks if the element is not read only */ protected abstract void checkWritable() throws SourceException; /** Retrieves cookie supported by the Element. In general, implementation * classes are ALWAYS available as cookies, so they can be extracted from both ElementImpls * and the abstract counterparts. */ public Node.Cookie getCookie(Class desired) { // return this instance, if the cookie is directly supported. if (desired.isAssignableFrom(getClass())) return this; // ask the CookieSet Node.Cookie ret = getCookieSet().getCookie(desired); if (ret != null) return ret; return getModelImpl().findElementCookie(getElement(), desired); } /** Finds the source element - the root for the hierarchy this element are part of. */ protected abstract SourceElementImpl findSource(); // Package-private protocol //////////////////////////////////////////////////////////////////////// protected void createAfter(Binding.Container cb, Binding refBinding) throws SourceException { inCreation = false; cb.insert(binding, refBinding); setValid(true); } protected abstract boolean parentValid(); public void notifyConnectionChange (Element old) { getModelImpl().fireElementChanged (old, getElement ()); } public void notifyConnectionAdd (Element newElem) { getModelImpl().fireElementAdded (newElem); } public void notifyConnectionRemove (Element removedElem) { getModelImpl().fireElementRemoved (removedElem); } public void notifyConnectionSet (Element oldElem, Element newElem) { getModelImpl().fireElementSet (oldElem, newElem); } protected void notifyCreate() { inCreation = false; setValid(true); if (parentValid()) notifyElementCreated(); } protected void notifyElementCreated() { getModelImpl().getEventQueue().elementCreated(getElement()); } protected void setJavaDocText(String content, boolean raw) throws SourceException { if (!(javaElement instanceof Feature)) { System.err.println ("JavaDoc not supported for this element: " + getClass ().getName ()); System.out.println ("JavaDoc not supported for this element: " + getClass ().getName ()); // NOI18N return; } checkWritable(); checkDocument(); boolean failed = true; repository.beginTrans (true); try { setClassPath(); String oldContent = raw ? javadoc.getRawText() : javadoc.getText(); if (content == oldContent || (content != null && oldContent != null && content.equals(oldContent))) { failed = false; checkIsValid (); return; } javadoc.changeJavaDocText(content, raw); ((Feature) javaElement).setJavadocText(javadoc.getRawText ()); failed = false; } catch (InvalidObjectException e) { throwIsInvalid (); } finally { repository.endTrans (failed); } } protected void changeJavaDocTags(JavaDocTag[] tags, int action) throws SourceException { if (!(javaElement instanceof Feature)) { System.err.println ("JavaDoc not supported for this element: " + getClass ().getName ()); System.out.println ("JavaDoc not supported for this element: " + getClass ().getName ()); // NOI18N return; } checkWritable(); checkDocument(); boolean failed = true; repository.beginTrans (true); try { setClassPath(); String oldContent = javadoc.getRawText(); javadoc.changeJavaDocTags(tags, action); ((Feature) javaElement).setJavadocText(javadoc.getRawText ()); failed = false; } catch (InvalidObjectException e) { throwIsInvalid (); } finally { repository.endTrans (failed); } } public void fireJavaDocChange () { fireOwnPropertyChange (new PropertyChangeEvent(getEventSource(), PROP_JAVADOC, null, null)); } /** * Notifies the element that it has been removed. */ protected void notifyRemove() { invalidate(); getModelImpl().getEventQueue().elementRemoved(getElement()); } /** * Checks whether the element can be removed. Fires a Vetoable property change * event on PROP_VALID from true to false. */ protected void checkRemove() throws SourceException { if (isCreated() || !isConstrained()) return; PropertyChangeEvent evt = new PropertyChangeEvent(getElement(), PROP_VALID, Boolean.TRUE, Boolean.FALSE); checkVetoablePropertyChange(evt); } /** Determines whether there's somebody interested in the property. Subclasses may * then optimize whether they should ever generate PropertyChangeEvent. */ protected boolean hasListeners(String propName) { if (vetoSupport.hasListeners(propName)) return true; if (propListeners == null) return false; synchronized (propListeners) { return !propListeners.isEmpty(); } } /** * Attempts to run atommically an operation. Throws SourceException if the runnable * throws any kind of exception * @deprecated use {@link #takeLock}/{@link #releaseLock} instead. */ protected void runAtomic(ExceptionRunnable r) throws SourceException { model.runAtomic(r); } protected void initializeListenerSupport() { } /** * Implementation of {@link ElementEvents} interface; returns the element to * be reported as the source of events. */ public final Object getEventSource() { return getElement(); } /** * Implementation of {@link ElementEvents} interface; returns the implementation * object paired with the event source (this object). */ public final ElementImpl getElementImpl() { return this; } protected abstract void setParent(ElementImpl parent); protected void setParent(Element parent) { setParent((ElementImpl)parent.getCookie(ElementImpl.class)); } /** * Convenience method that retrieves reference to the model that had created * the element. */ protected final DefaultLangModel getModelImpl() { return this.model; } /** * Attempts to obtain model's write lock preventing other threads from modifying * the model. Before it returns, the method also checks whether the element * is still valid. If not, it releases the lock and throws a SourceException * @return token object that should be later used to free the lock. */ protected final Object takeLock() { if (isCreated()) return null; Object o = getModelImpl().writeLock(); return o; } /** * Releases write lock on the model. If an invalid token object is passed, * the model will throw IllegalArgumentException * @param o token object for lock release operation. */ protected final void releaseLock(Object o){ if (isCreated()) return; getModelImpl().releaseWriteLock(o); } protected final void takeReadLock() { getModelImpl().readLock(); } protected final void releaseReadLock() { getModelImpl().releaseReadLock(); } /** * Takes a master lock. This lock differs from the ordinary in that it * does not create nested event queue, but merges all events to the current one. * Until the master lock is in effect, all events will be routed to the current * event queue. The master lock is used when the model's implementation is about * to issue nested model operations. */ protected final Object takeMasterLock() throws SourceException { if (isCreated()) return null; Object l = getModelImpl().masterWriteLock(); checkValid(l); return l; } protected abstract void createFromModel(Element model) throws SourceException; /** * Causes changes made by the last locked operation to be confirmed and, * upon lock release, merged into the higher operation. */ protected final void commit() { if (!isValid()) return; getModelImpl().commitChanges(); } /** * Returns true, if constraints on elements should be checked. This can * be disabled during some special operations (like external model updates). * If the method returns false, no VetoableChangeListeners should be informed. * @return true, if constraints are enabled, false otherwise. */ protected final boolean isConstrained() { return getModelImpl().isConstrained(); } protected Identifier createLocalIdentifier(Identifier id, int status) { if (id.getResolutionStatus() == status) return id; return Identifier.create(id.getFullName(), id.getSourceName(), status); } protected boolean checkIdentifierContext(Identifier id) { return false; } // .......................................................................... // a method named getElement already exists in ElementImpl, thus JMIElementCookie cannot be implemented directly class JMIElementCookieImpl implements JMIElementCookie { public org.netbeans.jmi.javamodel.Element getElement() { return (org.netbeans.jmi.javamodel.Element) getJavaElement(); } } // .......................................................................... static abstract class ElementListener implements MDRChangeListener { public static boolean REGISTER_LISTENER = true; protected RefObject javaElement; protected LWeakReference ref; protected ElementImpl impl; protected Object source; public boolean isValid = true; ElementListener (ElementImpl impl) { javaElement = impl.getJavaElement (); ref = new LWeakReference (impl, this); } public ElementImpl getImpl () { return (ElementImpl) ref.get(); } public RefObject getJavaElement () { return javaElement; } public void connect () { if (REGISTER_LISTENER) { for (Iterator iter = ((InstanceHandler) javaElement).getListeners().iterator(); iter.hasNext(); ) { Object listener = iter.next(); if (listener instanceof ElementListener) { ((ElementListener) listener).checkValidity(); } } ((MDRChangeSource) javaElement).addListener (this); isValid = true; } } public void remove () { try { ((MDRChangeSource) javaElement).removeListener (this); } catch (InvalidObjectException e) { } isValid = false; } void doRemove() { MDRepository repository = JavaMetamodel.getDefaultRepository(); repository.beginTrans(false); try { remove (); } finally { repository.endTrans(false); } } public void checkValidity() { if (ref.get() == null) { doRemove (); } } public final void change(MDRChangeEvent event) { if (!isValid) return; try { impl = (ElementImpl) ref.get(); if (impl == null) { doRemove(); return; } source = event.getSource (); if (source == javaElement) { if (event.isOfType (InstanceEvent.EVENT_INSTANCE_DELETE)) { doRemove (); impl.setValid (false); return; } } try{ doChange (event); } catch (InvalidObjectException e) { doRemove(); impl.setValid(false); } impl = null; source = null; } catch (RuntimeException e) { System.out.println("Exception in Listener.change () thrown !!"); // NOI18N e.printStackTrace (); throw e; } } public void doChange (MDRChangeEvent event) { if ((source instanceof Feature) && (event instanceof AttributeEvent) && "javadoc".equals(((AttributeEvent) event).getAttributeName ())) { // NOI18N impl.fireJavaDocChange (); } } } // .......................................................................... static class LWeakReference extends WeakReference implements Runnable { private ElementListener listener; public LWeakReference (Object ref, ElementListener listener) { super (ref, Utilities.activeReferenceQueue ()); this.listener = listener; } public void run() { try { listener.doRemove(); } catch (InvalidObjectException e) { } } } }

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