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

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.lang.reflect.Modifier;
import java.util.*;
import org.netbeans.jmi.javamodel.ClassDefinition;
import org.netbeans.jmi.javamodel.JavaClass;
import org.netbeans.jmi.javamodel.JavaModelPackage;
import org.netbeans.jmi.javamodel.Parameter;
import org.netbeans.jmi.javamodel.ParameterClass;
import org.netbeans.jmi.javamodel.ParameterizedType;
import org.netbeans.jmi.javamodel.ParameterizedTypeClass;
import org.netbeans.jmi.javamodel.Resource;
import org.netbeans.jmi.javamodel.TypeClass;

import org.netbeans.modules.javacore.jmiimpl.javamodel.ParameterizedTypeImpl.Wrapper;

import org.openide.NotifyDescriptor;
import org.openide.cookies.SourceCookie;
import org.openide.src.*;
import org.openide.util.RequestProcessor;
import org.openide.util.Task;
import org.openide.util.TaskListener;

import org.netbeans.modules.java.bridge.CommitListener;

import org.netbeans.modules.java.JavaConnections;
import org.netbeans.modules.java.JavaConnections.ChangeProcessor;
import org.netbeans.modules.java.settings.JavaSynchronizationSettings;

import org.netbeans.api.java.comparators.JavaElementComparator;
import org.netbeans.jmi.javamodel.Method;
import org.netbeans.modules.java.tools.JMIInheritanceSupport;
import org.netbeans.modules.java.tools.JMIInheritanceSupport.MethodKey;
import org.netbeans.modules.javacore.internalapi.JMIElementCookie;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.openide.DialogDisplayer;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;

/**
 * This class implements a dependency manager between a class and its ancestors.
 * Currently, the implementation is designed as a stateless helper that only separates
 * class-related logic from registration logic (located in {@link SourceConnectionSupport}).
 * It is possible, that the class will be converted into stateful (with the ClassElement bean
 * as the state info) and will manage only the bean's dependencies.
 *
 * @author  sdedic
 * @version 
 */
class ClassDependencyImpl implements PropertyChangeListener, CommitListener {
    private static final int MODIFIERS_ACCESS = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
            
    private static org.netbeans.modules.java.ui.nodes.elements.ElementFormat methodFormat;
    
    private static final boolean DEBUG = false;
    
    Registrar registrar;
    
    private JavaSynchronizationSettings SYNC_SETTINGS = null;

    private static Comparator CHANGEITEM_COMPARATOR;

    public ClassDependencyImpl(Registrar r) {
        if (SYNC_SETTINGS==null)
            SYNC_SETTINGS=(JavaSynchronizationSettings)JavaSynchronizationSettings.findObject(JavaSynchronizationSettings.class,true);
        if (CHANGEITEM_COMPARATOR == null)
            CHANGEITEM_COMPARATOR = new ChangeItemComparator();
        this.registrar = r;
    }
    
    private Collection getDirectSupertypes(ClassElement c) {
        Collection col = new LinkedList();
        col.addAll(Arrays.asList(c.getInterfaces()));
        if (c.getSuperclass() != null) {
            col.add(c.getSuperclass());
        }
        return col;
    }
    
    private void destroy(ClassElement c) {
        if (DEBUG)
            System.err.println("Class " + c.getName().getFullName() + " destructed."); // NOI18N
        Collection col = getDirectSupertypes(c);
        supertypesRemoved(c, col);
        // detach from the class - it is gone for good.
        c.removePropertyChangeListener(this);
    }
    
    private void registerNewClass(ClassElement c) {
        if (DEBUG)
            System.err.println("Class " + c.getName().getFullName() + " constructed."); // NOI18N
        Collection col = getDirectSupertypes(c);
        supertypesAdded(c, col, false);
        c.addPropertyChangeListener(this);
    }
    
    /////////////////////////////////////////////////////////////////////////////
    public void propertyChange(PropertyChangeEvent evt) {
        String propName = evt.getPropertyName();
        if (ElementProperties.PROP_INTERFACES.equals(propName))
            interfacesChanged(evt);
        else if (ElementProperties.PROP_SUPERCLASS.equals(propName))
            superclassChanged((ClassElement)evt.getSource(),
                (Identifier)evt.getOldValue(), (Identifier)evt.getNewValue());
        else if (ElementProperties.PROP_CLASS_OR_INTERFACE.equals(propName)) {
            ClassElement c = (ClassElement)evt.getSource();
            if (c.isClassOrInterface()) {
                supertypesAdded(c, getDirectSupertypes(c), false);
            }
        }
    }
    
    /**
     * Does a full refresh on a particular class' data. Mainly used when the class
     * is first seen (not constructed) in the source - e.g. after the first parse.
     */
    public void refreshClass(ClassElement c, boolean first) {
        if (DEBUG)
            System.err.println("Refreshing class " + c.getName().getFullName()); // NOI18N
        if (!first) {
            Collection sups = getDirectSupertypes(c);
            supertypesAdded(c, sups, first);
        }
        // just in case
        synchronized (this) {
            c.removePropertyChangeListener(this);
            c.addPropertyChangeListener(this);
        }
    }

    void synchronizeClass(ClassElement target) {
        JMIElementCookie cookie = (JMIElementCookie) target.getCookie(JMIElementCookie.class);
        if (cookie == null)
            return;
        ClassDefinition cd = (ClassDefinition) cookie.getElement();
        if ((cd instanceof JavaClass) && ((JavaClass) cd).isInterface())
            return; // do not synchronize interfaces
        
        List interfaces = cd.getInterfaces();
        Collection c = new ArrayList(interfaces.size() + 1);
        for (Iterator iter = interfaces.iterator(); iter.hasNext(); ) {
            c.add(iter.next());
        }
        JavaClass superClass = cd.getSuperClass();
        if (superClass != null)
            c.add(superClass);
        
        // get the metadata in sync with the current state.
        supertypesAdded(target, c, true);
        
        String caption = java.text.MessageFormat.format(
            getString("MSG_SynchronizeExplicit"), // NOI18N
            new Object[] {
                target.getName().getFullName(), "" // NOI18N
            });
        // nevertheless, synchronize!
        Synchronizer sync = new Synchronizer(target, c);
        sync.overrideDisable();
        sync.setCaption(caption);
        
        RequestProcessor.postRequest(sync);
        
    }
    
    private class Synchronizer implements Runnable {
        ClassElement target;
        Collection superIDs;
        boolean overrideDisable;
        int mode;
        Collection changeList;
        String caption;
        
        Synchronizer(ClassElement target, Collection superIDs) {
            this.target = target;
            this.superIDs = superIDs;
            this.mode = 1;
        }
        
        Synchronizer(Collection changeList) {
            this.changeList = changeList;
            this.mode = 2;
        }
        
        public void overrideDisable() {
            overrideDisable = true;
        }
        
        public void setCaption(String caption) {
            this.caption = caption;
        }
        
        public void run() {
            switch (mode) {
                case 1:
                    collectSupertypeMethods();
                    break;
                case 2:
                    confirmChanges();
            }
        }
        
        private void collectSupertypeMethods() {
            if (DEBUG)
                System.err.println("collecting supertype methods"); // NOI18N
            Collection c = implementFromSuper(target, superIDs);
            if (c == null || c.isEmpty())
                return;
            applyChanges(target, caption, c, overrideDisable);
        }
        
        private void confirmChanges() {
            autoApplyChanges(changeList);
        }
    }
        
    /**
     * Handles action(s) for removed interfaces. Removes them from the
     * "registered set", mainly.
     */
    protected void supertypesRemoved(ClassElement c, Collection removed) {
        for (Iterator it = removed.iterator(); it.hasNext(); ) {
            Identifier id = (Identifier)it.next();
            if (DEBUG)
                System.err.println("Removing dependency: " + c.getName().getFullName() + " -> " + id.getFullName()); // NOI18N
            registrar.removeDependency(c, id);
        }
    }
    
    protected void supertypesAdded(final ClassElement c, final Collection added, 
        boolean refreshOnly) {
        Collection resolved = null;
        
        if (c.isClass()) {
            for (Iterator it = added.iterator(); it.hasNext(); ) {
                Object obj = it.next();
                Identifier id = null;
                if (obj instanceof JavaClass) {
                    ClassElement clsElem = getClassElement((JavaClass) obj);
                    id = clsElem.getName();
                } else {
                    id = (Identifier)obj;
                    String fqn = id.getFullName();
                    int index = fqn.indexOf('<');
                    if (index > -1) {
                        id = Identifier.create(fqn.substring(0, index));
                    }
                }
                if (resolved == null)
                    resolved = new LinkedList();
                if (registrar.addDependency(c, id)) {
                    resolved.add(id);
                }
            }
        }
        if (resolved == null || resolved.isEmpty())
            return;

        if (refreshOnly || !c.isClassOrInterface() || (c.getModifiers() & Modifier.ABSTRACT) > 0)
            return;

        // PENDING: handle new interfaces.
        int syncMode = getSynchronizationMode(c);
        
        if (syncMode == SourceConnectionSupport.CONNECT_NOT)
            return;

        String caption = java.text.MessageFormat.format(
            getString("MSG_SynchronizeAddInterfaces"), // NOI18N
            new Object[] {
                c.getName().getFullName(), "" // NOI18N
            });
            
        Synchronizer sync = new Synchronizer(c, resolved);
        sync.setCaption(caption);
        RequestProcessor.postRequest(sync);
    }
    
    private void interfacesChanged(PropertyChangeEvent evt) {
        MultiPropertyChangeEvent e = (MultiPropertyChangeEvent)evt;
        MultiPropertyChangeEvent addEvent = null, removeEvent = null;

        for (Iterator changes = (Iterator)e.getIterator(); changes.hasNext(); ) {
            MultiPropertyChangeEvent part = (MultiPropertyChangeEvent)changes.next();
            int type = part.getEventType();
            
            if (type == MultiPropertyChangeEvent.TYPE_REMOVE) {
                removeEvent = part;
            } else if (type == MultiPropertyChangeEvent.TYPE_ADD) {
                addEvent = part;
            }
        }
        
        if (removeEvent != null)
            supertypesRemoved((ClassElement)evt.getSource(), removeEvent.getAffectedItems());
        if (addEvent != null)
            supertypesAdded((ClassElement)evt.getSource(), addEvent.getAffectedItems(), false);
    }
    
    public boolean dependsOn(ClassElement c, Identifier id) {
        Identifier s = c.getSuperclass();
        if (id == null)
            return false;
        if (s != null && 
            s.compareTo(id, false))
            return true;
        Identifier[] ss = c.getInterfaces();
        for (int i = 0; i < ss.length; i++) {
            s = ss[i];
            if (s.compareTo(id, false))
                return true;
        }
        return false;
    }
    
    private void superclassChanged(ClassElement c, Identifier old, Identifier now) {
        if (old != null)
            supertypesRemoved(c, Collections.singletonList(old));
        if (now != null)
            supertypesAdded(c, Collections.singletonList(now), false);
    }

    public void changesCommited(Set added, Set removed, Map changed) {
        if (removed != null) {
            for (Iterator it = removed.iterator(); it.hasNext(); ) {
                Element el = (Element)it.next();
                if (!(el instanceof ClassElement))
                    continue;
                ClassElement c = (ClassElement)el;
                destroy(c);
            }
        }
        if (added != null) {
            for (Iterator it = added.iterator(); it.hasNext(); ) {
                Element el = (Element)it.next();
                if (!(el instanceof ClassElement))
                    continue;
                ClassElement c = (ClassElement)el;
                registerNewClass(c);
            }
        }
    }
    
    private static Method findMatchingMethod(ClassDefinition jmiClazz, Method jmiElem,
        boolean superclasses, boolean acceptAbstract) {        
        
        List params = jmiElem.getParameters();
        List paramTypes = new ArrayList(params.size());
        for (Iterator iter = params.iterator(); iter.hasNext(); ) {
            paramTypes.add(((org.netbeans.jmi.javamodel.Parameter) iter.next()).getType());
        }
        Method m = jmiClazz.getMethod(jmiElem.getName(), paramTypes, false);
        // if we do not accept abstract methods and the method is, invalidate
        if (!acceptAbstract && (m != null && (m.getModifiers() & Modifier.ABSTRACT) > 0)) {
            m = null;
        }
        // end the search if a method was found, or the search should not 
        // continue to parents.
        if (m != null || !superclasses)
            return m;

        jmiClazz = jmiClazz.getSuperClass();
        if (jmiClazz == null)
            return null;
        return findMatchingMethod(jmiClazz, jmiElem, superclasses, false);
    }
    
    private static Method findMatchingMethod(ClassDefinition jmiClazz, MethodElement elem, MethodElement newElem,
        Method newMethod, boolean superclasses, boolean acceptAbstract) {        
        
        MethodParameter[] params = elem.getParameters();
        MethodParameter[] newParams = newElem.getParameters();        
        String[] typeNames = new String [params.length];
        boolean sameParams = params.length == newParams.length;
        if (sameParams) {
            for (int x = 0; x < params.length; x++) {
                if (!newParams[x].getType().getFullString().equals(params[x].getType().getFullString())) {
                    sameParams = false;
                    break;
                }
            }
        }
        if (sameParams) {
            Iterator it = newMethod.getParameters().iterator();
            for (int x = 0; it.hasNext(); x++) {
                typeNames[x] = ((Parameter) it.next()).getType().getName();
            }
        } else {
            for (int x = 0; x < params.length; x++) {
                typeNames[x] = params[x].getType().getFullString();
            }
        }
        return findMatchingMethod(jmiClazz, typeNames, elem.getName().getName(), superclasses, acceptAbstract);
    }
    
    private static Method findMatchingMethod(ClassDefinition jmiClazz, String[] typeNames, String name, boolean superclasses, boolean acceptAbstract) {
        Method m = null;
        for (Iterator iter = jmiClazz.getContents().iterator(); iter.hasNext(); ) {
            Object o = iter.next();
            if (o instanceof Method) {
                Method m_1 = (o instanceof Wrapper) ? (Method)((Wrapper) o).getWrappedObject() : (Method)o;
                if (!name.equals(m_1.getName()))
                    continue;
                List pars = m_1.getParameters();
                if (typeNames.length != pars.size())
                    continue;
                boolean matches = true;
                Iterator it = pars.iterator();
                for (int i = 0; i < typeNames.length; i++) {
                    String tName = ((Parameter)it.next()).getType().getName();
                    if (!typeNames[i].equals(tName)) {
                        matches = false;
                        break;
                    }
                }
                if (matches) {
                    m = m_1;
                    break;
                }
            }
        }
        // if we do not accept abstract methods and the method is, invalidate
        if (!acceptAbstract && (m != null && (m.getModifiers() & Modifier.ABSTRACT) > 0)) {
            m = null;
        }
        // end the search if a method was found, or the search should not 
        // continue to parents.
        if (m != null || !superclasses)
            return m;

        jmiClazz = jmiClazz.getSuperClass();
        if (jmiClazz == null)
            return null;
        return findMatchingMethod(jmiClazz, typeNames, name, superclasses, false);
    }
    
    private static ClassDefinition findMatchingClass(ClassDefinition target, ClassDefinition cd) {
        List interfaces = target.getInterfaces();
        List supers = new ArrayList(interfaces.size() + 1);
        supers.addAll(interfaces);
        JavaClass superCls = target.getSuperClass();
        if (superCls != null)
            supers.add(superCls);
        for (Iterator iter = supers.iterator(); iter.hasNext(); ) {
            JavaClass jc = (JavaClass) iter.next();
            JavaClass jcls = jc instanceof ParameterizedType ? ((ParameterizedType) jc).getDefinition() : jc;
            if (jcls == cd)
                return jc;
        }
        for (Iterator iter = supers.iterator(); iter.hasNext(); ) {
            JavaClass jc = (JavaClass) iter.next();
            ClassDefinition res = findMatchingClass(jc, cd);
            if (res != null)
                return res;
        }
        return null;
    }

    private static Method findMethod(ClassDefinition jc, Method ns) {
        for (Iterator iter = jc.getContents().iterator(); iter.hasNext(); ) {
            Object o = iter.next();
            if (o instanceof Method) {
                Method m_1 = (o instanceof Wrapper) ? (Method)((Wrapper) o).getWrappedObject() : (Method)o;
                if (m_1 == ns) {
                    return (Method)o;
                }
            }
        }
        return null;
    }
    
    private static ClassElement getClassElement(ClassDefinition cd) {
        if (cd instanceof ParameterizedType) {
            cd = ((ParameterizedType) cd).getDefinition();
        }
        DataObject dobj = JavaMetamodel.getManager().getDataObject(cd.getResource());
        SourceCookie sourceCookie = (SourceCookie) dobj.getCookie(SourceCookie.class);
        if (sourceCookie == null)
            return null;
        SourceElement selem = sourceCookie.getSource();
        ClassElement[] cls = selem.getClasses();
        for (int x = 0; x < cls.length; x++) {
            if (cd == ((JMIElementCookie)cls[x].getCookie(JMIElementCookie.class)).getElement())
                return cls[x];
            ClassElement res = findInnerClass(cls[x], cd);
            if (res != null)
                return res;
        }
        return null;
    }
    
    private static ClassElement findInnerClass(ClassElement c, ClassDefinition cd) {
        ClassElement[] inners = c.getClasses();
        for (int x = 0; x < inners.length; x++) {
            if (cd == ((JMIElementCookie)inners[x].getCookie(JMIElementCookie.class)).getElement())
                return inners[x];
            ClassElement res = findInnerClass(inners[x], cd);
            if (res != null)
                return res;
        }
        return null;
    }
    
    public static MethodElement getMethodElement(ClassElement clazz, Method m, ClassDefinition cd) {
        int index = -1;
        boolean found = false;
        for (Iterator iter = cd.getContents().iterator(); iter.hasNext(); ) {
            Object obj = iter.next();
            if (obj instanceof Method) {
                index++;
                if (obj == m) {
                    found = true;
                    break;
                }
            }
        }
        MethodElement[] melems = clazz.getMethods();
        return found ? melems[index] : null;
    }
    
    public static ClassElement forName(String name, TypeClass typeProxy, ParameterizedTypeClass ptypeProxy) {
        JavaClass jc = resolve(name, typeProxy, ptypeProxy);
        if (jc == null)
            return null;
        return getClassElement(jc);
    }
    
     private static JavaClass resolve(String fullName, TypeClass typeProxy, ParameterizedTypeClass ptypeProxy) {
        int index = fullName.indexOf('<');
        if (index == -1) {
            return (JavaClass)typeProxy.resolve(fullName);
        }
        JavaClass jc = (JavaClass)typeProxy.resolve(fullName.substring(0, index));
        List arguments = new LinkedList();
        int size = fullName.length();
        int i = index + 1;
        int level = 0;
        while (i < size && level >= 0) {
            char c = fullName.charAt(i);
            switch (c) {
                case '<':
                    level++;
                    break;
                case '>':
                    if (level == 0) {
                        arguments.add(resolve(fullName.substring(index + 1, i), typeProxy, ptypeProxy));
                    }
                    level--;
                    break;
                case ',':
                    if (level == 0) {
                        arguments.add(resolve(fullName.substring(index + 1, i), typeProxy, ptypeProxy));
                        index = i;
                    }
            } // switch
            i++;
        } // while
        return ptypeProxy.resolveParameterizedType(jc, arguments, null); // [PENDING] outerclass param ??
    }
    
    private static class AddProcessor extends ChangeProcessor {
        private ClassDefinition targetClass;
        Method addMethod;
        private SourceException excpt;
        
        AddProcessor(String description, Method m, ClassDefinition target, MethodElement model) {
            super(description, model);
            addMethod = m;
            targetClass = target;
        }
        
        public void process() {
            JMIInheritanceSupport is = new JMIInheritanceSupport(targetClass);
            is.overrideMethod(addMethod, true, false);
        }
        
    }
    
    private static class UpdateProcessor extends ChangeProcessor {
        private Method target;
        Method m;
        
        UpdateProcessor(String message, Method target, Method m, MethodElement melem) {
            super(message, melem);
            this.target = target;
            this.m = m;
        }
        
        public void process() {
            // update modifiers, if necessary:
            
            int mods = target.getModifiers() & ~ (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE);
            if (((JavaClass)m.getDeclaringClass()).isInterface()) {
                mods |= m.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE);
                mods &= ~Modifier.ABSTRACT;
            } else {
                mods |= Modifier.PUBLIC;
            }
            target.setModifiers(mods);
            
            // update name:
            target.setName(m.getName());
            
            // update return type
            target.setType(m.getType());
            
            // update parameters:
            JavaModelPackage modelPackage = (JavaModelPackage)target.refImmediatePackage();
            ParameterClass parProxy = modelPackage.getParameter();
            
            List pars1 = target.getParameters();
            List pars2 = m.getParameters();
            List targetPars = null;
            
            if (pars2.size() != pars1.size())
                targetPars = duplicateParameters(pars2, parProxy);
            else {
                Iterator it_1 = pars1.iterator();
                Iterator it_2 = pars2.iterator();
                int size = pars1.size();
                for (int i = 0; i < size; i++) {
                    Parameter p1 = (Parameter) it_1.next();
                    Parameter p2 = (Parameter) it_2.next();
                    if (!p1.getType().equals(p2.getType())) {
                        targetPars = duplicateParameters(pars2, parProxy);
                        break;
                    }
                }
            }
            if (targetPars != null) {
                pars1.clear();
                pars1.addAll(targetPars);
            }
        }
    }
    
    private static List duplicateParameters(List params, ParameterClass parProxy) {
        List result = new ArrayList(params.size());
        for (Iterator iter = params.iterator(); iter.hasNext(); ) {
            Parameter p = (Parameter) iter.next();
            Parameter newPar = parProxy.createParameter(
                p.getName(), null, p.isFinal(), null, 0, p.isVarArg()
            );
            newPar.setType(p.getType());
            result.add(newPar);
        }
        return result;
    }        
    
    private static String getString(String key) {
        return SourceConnectionSupport.getString(key);
    }
    
    private static ChangeProcessor createUpdateProcessor(Method desired, Method c, Method found, MethodElement model) {
        boolean same = true;
        int access;
        
        same = desired.getName().equals(found.getName()) &&
            desired.getType().getName().equals(found.getType().getName()) &&
            desired.getParameters().size() == found.getParameters().size();
        ClassDefinition cd = desired.getDeclaringClass();
        if (cd instanceof JavaClass && ((JavaClass) cd).isInterface()) {
            access = desired.getModifiers() & MODIFIERS_ACCESS;
            // PENDING: have the implementing class obey access rules!!
        }  else {
            access = Modifier.PUBLIC;
        }
        int foundAccess = found.getModifiers() & MODIFIERS_ACCESS;
        same &= (foundAccess == access) ||
                (foundAccess == Modifier.PUBLIC && access == Modifier.PROTECTED) ||
                (foundAccess == Modifier.PROTECTED && access == 0);
                      
        if (same) {
            Iterator pars1 = desired.getParameters().iterator();
            Iterator pars2 = found.getParameters().iterator();
            while (same && pars1.hasNext()) {
                same = ((Parameter)pars1.next()).getType().getName().equals(
                    ((Parameter)pars2.next()).getType().getName());
            }
        }
        if (same)
            return null;

        /*
        if (c == null)
            c = (Method)desired.duplicate();
        c.setModifiers((found.getModifiers() & ~MODIFIERS_ACCESS) | access);
        // bugfix #27614 -- javadocs should not be syncrhonized
        c.setJavadoc(null);
         */
            
        String updateFormat= getString("MSG_UpdateMethod"); // NOI18N
        String msg = java.text.MessageFormat.format(updateFormat, 
            new Object[] { formatMethod(found), formatMethod(desired), 
                desired.getDeclaringClass().getName()
            });
    
        return new UpdateProcessor(msg, found, desired, model);
    }
    
    private static ChangeProcessor createAddProcessor(ClassDefinition target, Method t, MethodElement model) {
        // don't add nonabstract methods of superclasses
        ClassDefinition cd = t.getDeclaringClass();
        boolean isClass = !(cd instanceof JavaClass) || !((JavaClass) cd).isInterface();
        if (isClass && (t.getModifiers() & Modifier.ABSTRACT) == 0)
            return null;
        
        /*
        if (t.getDeclaringClass().isInterface()) {
            c.setModifiers(
                (c.getModifiers() & ~(Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE)) |
                Modifier.PUBLIC
            );
        } 
         */ // [PENDING]       
        String addMessageFormat = getString("MSG_AddMethod"); // NOI18N
        String msg = java.text.MessageFormat.format(addMessageFormat, 
            new Object[] { cd.getName(), formatMethod(t) }
            );
        
        return new AddProcessor(msg, t, target, model);
    }
    
    private static String formatMethod(Method m) {
        if (methodFormat == null) {
            methodFormat = new org.netbeans.modules.java.ui.nodes.elements.ElementFormat(getString("FMT_MethodSignature"));
        }
        return methodFormat.format(m);
    }
    
    public void connectionNotify(ClassElement target, ClassElement source, 
        Enumeration newItems, Enumeration changeEvents) {
        if (DEBUG) {
            System.err.println("[" + target.getName().getFullName() + "] got connectionNotify. "); // NOI18N
        }
        
        int syncMode = getSynchronizationMode(target);
        
        if (syncMode == SourceConnectionSupport.CONNECT_NOT)
            return;
        if (!target.isClassOrInterface()) 
            return;
        
        final Collection changes = synchronizeMethods(target, newItems, changeEvents, true);
        if (DEBUG) {
            if (changes == null) {
                System.err.println("no changes required"); // NOI18N
            } else 
                System.err.println("identified " + changes.size() + " changes"); // NOI18N
        }
        if (changes == null || changes.isEmpty())
            return;
        final String caption = java.text.MessageFormat.format(getString("MSG_SynchronizeCaption"),
            new Object[] {
                target.getName().getFullName(), source.getName().getFullName()
            });
        if (DEBUG) {
            System.err.println("caption for the dialog = " +  caption); // NOI18N
        }
        applyChanges(target, caption, changes);
    }

    private void applyChanges(final ClassElement target, final String caption, final Collection changes) {
        applyChanges(target, caption, changes, false);
    }
    
    private void applyChanges(final ClassElement target, final String caption, Collection changes,
        boolean ignoreNot) {
        final int syncMode = getSynchronizationMode(target);

        if (!ignoreNot && (syncMode == SourceConnectionSupport.CONNECT_NOT || !SYNC_SETTINGS.isEnabled()))
            return;

        Collections.sort((List)changes,CHANGEITEM_COMPARATOR);
        if (syncMode == SourceConnectionSupport.CONNECT_AUTO) {
            RequestProcessor.postRequest(new Synchronizer(changes));
        } else {
            List l = new ArrayList(changes);
            JavaConnections.SyncRequest req = JavaConnections.scheduleShowChangesDialog(
                caption, l, (byte)syncMode);
            req.getTask().addTaskListener(new SyncRequestListener(target, req));
        }
    }
    
    private void autoApplyChanges(Collection changes) {
        Collection failed = null;
        
        for (Iterator it = changes.iterator(); it.hasNext(); ) {
            ChangeProcessor proc = (ChangeProcessor)it.next();
            try {
                proc.process();
            } catch (SourceException ex) {
                if (failed == null)
                    failed = new LinkedList();
                failed.add(proc);
            }
        }
        if (failed == null)
            return;
        StringBuffer sb = new StringBuffer(java.text.MessageFormat.format(
            getString("MSG_ChangesFailed"),
            new Object[] { new Integer(failed.size()) }));
        for (Iterator it = failed.iterator(); it.hasNext(); ) {
            sb.append('\n');
            sb.append(((ChangeProcessor)it.next()).getDisplayName());
        }
        NotifyDescriptor desc = new NotifyDescriptor.Message(sb.toString(), 
            NotifyDescriptor.WARNING_MESSAGE);
        DialogDisplayer.getDefault().notify(desc);
    }
    
    private class SyncRequestListener implements TaskListener {
        JavaConnections.SyncRequest request;
        ClassElement target;
        
        SyncRequestListener(ClassElement t, JavaConnections.SyncRequest r) {
            request = r;
            target = t;
        }
        
        public void taskFinished(Task t) {
            t.removeTaskListener(this);
            setSynchronizationMode(target, request.getSyncType());
        }
    }
    
    private static class ChangeItemComparator implements Comparator {
        
        private static Comparator METHOD_COMPARATOR;
        
        ChangeItemComparator() {        
            if (METHOD_COMPARATOR == null)
                METHOD_COMPARATOR = JavaElementComparator.createConstructorComparator(true,
                   new int[] {JavaElementComparator.NAME | JavaElementComparator.PARAMETERS});
        }
        
        public int compare(Object obj, Object obj1) {
            Method m1 = null;
            Method m2 = null;
            if (obj instanceof UpdateProcessor) {
                m1 = ((UpdateProcessor) obj).m;
            } else {
                m1 = ((AddProcessor) obj).addMethod;
            }
            if (obj1 instanceof UpdateProcessor) {
                m2 = ((UpdateProcessor) obj1).m;
            } else {
                m2 = ((AddProcessor) obj1).addMethod;
            }            
            String s1 = m1.getName();
            String s2 = m2.getName();
            int code = s1.compareTo(s2);
            if (code != 0)
                return code;
            List params1 = m1.getParameters();
            List params2 = m2.getParameters();
            if (params1.size() < params2.size()) {
                return -1;
            } else if (params1.size() > params2.size()) {
                return 1;
            }
            Iterator iter1 = params1.iterator();
            Iterator iter2 = params2.iterator();
            while (iter1.hasNext()) {
                org.netbeans.jmi.javamodel.Type t1 = ((Parameter)iter1.next()).getType();
                org.netbeans.jmi.javamodel.Type t2 = ((Parameter)iter2.next()).getType();
                if (!t1.equals(t2)) {
                    return t1.getName().compareTo(t2.getName());
                }
            }
            return 0;
        }
        
    }
    
    protected Collection synchronizeMethods(ClassElement target, 
        Enumeration neededMethods, boolean deepScan) {
        return synchronizeMethods(target, neededMethods, null, deepScan);
    }
    
    public Collection synchronizeMethods(ClassElement target, Enumeration newItems,
        Enumeration changeEvents, boolean deepScan) {
        ClassDefinition cd = (ClassDefinition) ((JMIElementCookie) target.getCookie(JMIElementCookie.class)).getElement();
        return synchronizeMethods(cd, newItems, null, changeEvents, deepScan);
    }
    
    /**
     * Attempts to synchronized the class' implementation with the passed information.
     * @param newItems new methods that should be inserted into the class.
     * @param changeEvents list of changes that should be applied to the class.
     */
    private Collection synchronizeMethods(ClassDefinition target, Enumeration newItems, Enumeration modItems, 
        Enumeration changeEvents, boolean deepScan) {
        
        // for new items, try to locate the method in the target class.
        Collection changes = null;
        if (newItems != null) {
            while (newItems.hasMoreElements()) {
                Object o = newItems.nextElement();
                Object mo = modItems != null && modItems.hasMoreElements() ? modItems.nextElement() : null;
                Method m = null;
                Method c = null;
                Method m_1 = null;
                MethodElement model = null;
                ClassElement celem = null;
                
                if (o instanceof MethodElement) {
                    model = (MethodElement)o;
                    celem = model.getDeclaringClass();
                    m_1 = (Method)((JMIElementCookie)model.getCookie(JMIElementCookie.class)).getElement();
                    if (mo instanceof MethodElement) {
                        MethodElement moel = (MethodElement) mo;
                        c = (Method)((JMIElementCookie)moel.getCookie(JMIElementCookie.class)).getElement();
                    }
                    ClassDefinition cd = findMatchingClass(target, m_1.getDeclaringClass());
                    m = findMethod(cd, m_1);
                } else if (o instanceof Method) {
                    m = (Method)o;
                    c = (Method)mo;
                    m_1 = (m instanceof Wrapper) ? (Method)((Wrapper) m).getWrappedObject() : m;
                    ClassDefinition declClass = m_1.getDeclaringClass();
                    if (m != m_1 && declClass == m.getDeclaringClass()) {
                        m_1 = m;
                    }
                    celem = getClassElement(declClass); // [PENDING]
                    model = getMethodElement(celem, m_1, declClass);
                } else {
                    continue;
                }
                
                Method found = findMatchingMethod(target, m, deepScan, true);                
                ChangeProcessor proc = null;
                // PENDING: what if the method is abstract ??
                if (found != null) {
                    // now, the method may have been in one of the ancestors.
                    // this IS a problem, since if it is not EXACTLY the same (exception ids)
                    // we cannot modify it.
                    if (found.getDeclaringClass() != target) {
                        continue;
                    }
                    proc = createUpdateProcessor(m, c, found, model);
                    if (proc == null) {
                        // PENDING: link found to m so that the dependency can
                        // be maintained over time.
                        continue;
                    }
                    // the method IS present, but is slightly different.
                } else {
                    proc = createAddProcessor(target, m, model);
                }
                if (proc != null) {
                    if (changes == null)
                        changes = new LinkedList();
                    changes.add(proc);
                }
            }
        }
        
        // new items have been created. Now consult the changed ones.
        if (changeEvents != null) {
            while (changeEvents.hasMoreElements()) {
                JavaConnections.Change ch = (JavaConnections.Change)changeEvents.nextElement();
                if (ch.getChangeType() != JavaConnections.TYPE_METHODS_CHANGE) {
                    continue;
                }
                MethodElement oldStateElem = (MethodElement)ch.getOldElement();
                MethodElement newStateElem = (MethodElement)ch.getNewElement();
                // Method oldState = (Method) ((JMIElementCookie) oldStateElem.getCookie(JMIElementCookie.class)).getElement();
                Method ns = (Method) ((JMIElementCookie) newStateElem.getCookie(JMIElementCookie.class)).getElement();
                ClassDefinition jc = ns.getDeclaringClass();
                jc = findMatchingClass(target, jc);
                Method newState = findMethod(jc, ns);                
                
                ChangeProcessor proc = null;
                
                // first, try to search for the new method element, it may be present.
                Method found = findMatchingMethod(target, newState, deepScan, true);
                if (found != null) {
                    if (target != found.getDeclaringClass()) {
                        // something bad -- possibly conflicting method in a superclass.
                        // PENDING: warn the user.
                        continue;
                    }
                    proc = createUpdateProcessor(newState, null, found, oldStateElem);
                    if (proc == null) {
                        // the method is identical.
                        // PENDING: link the method to the discovered super's one.
                        continue;
                    }
                } else if ((found = findMatchingMethod(target, oldStateElem, newStateElem, newState, deepScan, true)) != null) {
                    if (target != found.getDeclaringClass()) {
                        // if uncompatible, it can pose a problem.
                        // PENDING: warn the user, if this is the case.
                        continue;
                    }
                    proc = createUpdateProcessor(newState, null, found, oldStateElem);
                    if (proc == null) {
                        // huh ? exactly the same method ?
                        continue;
                    }
                } else {
                    proc = createAddProcessor(target, newState, oldStateElem);
                }
                if (proc != null) {
                    if (changes == null)
                        changes = new LinkedList();
                    changes.add(proc);
                }
            }
        }
        
        return changes;
    }
    
    private void addSupertypeMethods_old(ClassElement c, Map methods, Map modMethods, Set seen, FileObject source) {
        if (c == null)
            return;
        if (seen.contains(c.getName().getFullName()))
            return;

        seen.add(c.getName().getFullName());
        if (c == null)
            return;
        
        Resource resource = JavaMetamodel.getManager().getResource(source);
        MethodElement[] ms = c.getMethods();
        boolean clazz = c.isClassOrInterface();
        
        if (DEBUG) {
            System.err.println("Collecting supertypes of " + c.getName().getFullName()); // NOI18N
        }
        
        ClassDefinition cd = (ClassDefinition) ((JMIElementCookie) c.getCookie(JMIElementCookie.class)).getElement();
        for (Iterator iter = cd.getInterfaces().iterator(); iter.hasNext(); ) {
            addSupertypeMethods((ClassDefinition) iter.next(), methods, modMethods, seen);
        }
        if (clazz && cd.getSuperClass() != null) {
            // adds all superclass' abstract methods.
            addSupertypeMethods(cd.getSuperClass(), methods, modMethods, seen);
        }
        
        if (DEBUG) {
            System.err.println("Adding methods of " + c.getName().getFullName()); // NOI18N
        }
        for (int i = 0; i < ms.length; i++) {
            MethodElement m = ms[i];
            MethodElement.Key key = new MethodElement.Key(m);
            if (!clazz || ((m.getModifiers() & Modifier.ABSTRACT) > 0)) {
                if (methods.containsKey(key)) {
                    if (DEBUG) {
                        System.err.println("Method" + ModelEventAdapter.reportElementID(m) + " already recorded"); // NOI18N
                    }
                    continue;
                }
                methods.put(key, m);
                modMethods.put(key, m);
                if (DEBUG) {
                    System.err.println("Adding " + ModelEventAdapter.reportElementID(m)); // NOI18N
                }
            } else {
                if (DEBUG) {
                    System.err.println("Implemented " + ModelEventAdapter.reportElementID(m)); // NOI18N
                }
                // un-register the method:
                if (methods.remove(key) != null) {
                    modMethods.remove(key);
                    if (DEBUG) {
                        System.err.println("-- removing from list."); // NOI18N
                    }
                }
            }
        }
        if (DEBUG) {
            System.err.println("Done processing of " + c.getName().getFullName()); // NOI18N
        }
    }

    private void addSupertypeMethods(ClassDefinition c, Map methods, Map modMethods, Set seen) {
        if (c == null)
            return;
        if (seen.contains(c))
            return;
        seen.add(c);                
        Resource resource = c.getResource();
        boolean isClazz = !(c instanceof JavaClass) || !((JavaClass) c).isInterface();
                
        for (Iterator iter = c.getInterfaces().iterator(); iter.hasNext(); ) {
            addSupertypeMethods((ClassDefinition) iter.next(), methods, modMethods, seen);
        }
        if (isClazz && c.getSuperClass() != null) {
            // adds all superclass' abstract methods.
            addSupertypeMethods(c.getSuperClass(), methods, modMethods, seen);
        }
        
        for (Iterator iter = c.getContents().iterator(); iter.hasNext(); ) {
            Object obj = iter.next();
            if (!(obj instanceof Method))
                continue;
            Method m = (Method) obj;
            MethodKey key = new MethodKey(m);
            if (!isClazz || ((m.getModifiers() & Modifier.ABSTRACT) > 0)) {
                if (methods.containsKey(key)) {
                    continue;
                }
                methods.put(key, m);
                modMethods.put(key, m);
            } else {
                // un-register the method:
                if (methods.remove(key) != null) {
                    modMethods.remove(key);                    
                }
            }
        }
    }
    
    private Collection implementFromSuper(ClassElement target, Collection supertypes) {
        Map methods = new HashMap(31);
        Map modMethods = new HashMap(31);
        ClassDefinition cd = (ClassDefinition)((JMIElementCookie)target.getCookie(JMIElementCookie.class)).getElement();
        JavaModelPackage pkg = (JavaModelPackage)cd.refOutermostPackage();
        for (Iterator it = supertypes.iterator(); it.hasNext(); ) {
            Object obj = it.next();
            JavaClass sjc = null;
            if (obj instanceof JavaClass) {
                sjc = (JavaClass) obj;
            } else {
                Identifier id = (Identifier)obj;
                sjc = resolve(id.getFullName(), pkg.getType(), pkg.getParameterizedType());
            }
            if (sjc != null) {
                addSupertypeMethods(sjc, methods, modMethods, new HashSet(11));
            }
        }
        Collection c = synchronizeMethods(cd, Collections.enumeration(
            methods.values()), Collections.enumeration(modMethods.values()), null, true);
        if (c == null || c.isEmpty())
            return null;
        else
            return c;
    }
    
    protected byte getSynchronizationMode(ClassElement t) {
        return (byte)registrar.getSynchronizationMode(t);
    }
    
    protected void setSynchronizationMode(ClassElement t, byte value) {
        registrar.setSynchronizationMode(t, value);
    }
}
... 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.