org.eclipse.jem.internal.adapters.jdom.JavaReflectionSynchronizer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jem.internal.adapters.jdom.JavaReflectionSynchronizer.java

Source

/*******************************************************************************
 * Copyright (c) 2001, 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jem.internal.adapters.jdom;
/*
    
    
 */

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.jdt.core.*;

import org.eclipse.jem.internal.plugin.JavaPlugin;
import org.eclipse.jem.util.logger.proxy.Logger;
import org.eclipse.jem.workbench.utility.JavaModelListener;

/**
 * Insert the type's description here.
 * Creation date: (11/1/2000 11:42:05 AM)
 * @author: Administrator
 */
public class JavaReflectionSynchronizer extends JavaModelListener {

    protected JavaJDOMAdapterFactory fAdapterFactory;

    protected boolean flushedAll = false;
    protected List flushTypes = new ArrayList();
    protected List flushTypePlusInner = new ArrayList();
    protected List notifications = new ArrayList();

    /**
     * JavaReflectionSynchronizer constructor comment.
     */
    public JavaReflectionSynchronizer(JavaJDOMAdapterFactory synchronizee) {
        super();
        fAdapterFactory = synchronizee;
    }

    /* (non-Javadoc)
     * @see org.eclipse.jem.internal.adapters.jdom.JavaModelListener#getJavaProject()
     */
    protected IJavaProject getJavaProject() {
        return getAdapterFactory().getJavaProject();
    }

    /**
     * Tell the reflection factory to flush the passed IType
     */
    protected Notification doFlush(IType element) {
        return getAdapterFactory().flushReflectionNoNotification(element.getFullyQualifiedName());
    }

    /*
     * Flush the compilation unit and any inner classes since we don't if they may or may not of changed.
     */
    protected Notification doFlush(ICompilationUnit element) {
        return getAdapterFactory().flushReflectionPlusInnerNoNotification(getFullNameFromElement(element));
    }

    protected void flush(IType element) {
        if (!flushTypes.contains(element))
            flushTypes.add(element);
    }

    /*
     * flush the compilation unit. Since we don't know if inner classes may also
     * of been affected, they to will be flushed.
     */
    protected void flush(ICompilationUnit element) {
        if (!flushTypePlusInner.contains(element))
            flushTypePlusInner.add(element);
    }

    protected void flushPackage(String packageName, boolean noFlushIfSourceFound) {
        notifications.addAll(getAdapterFactory().flushPackageNoNotification(packageName, noFlushIfSourceFound));
    }

    protected JavaJDOMAdapterFactory getAdapterFactory() {
        return fAdapterFactory;
    }

    /**
     * Handle the change for a single element, children will be handled separately.
     *
     */
    protected void processJavaElementChanged(ICompilationUnit element, IJavaElementDelta delta) {
        switch (delta.getKind()) {
        case IJavaElementDelta.CHANGED: {
            // A file save had occurred. It doesn't matter if currently working copy or not.
            // It means something has changed to the file on disk, but don't know what.
            if ((delta.getFlags() & IJavaElementDelta.F_PRIMARY_RESOURCE) != 0) {
                flush(element); // Flush everything, including inner classes.               
            } else if ((delta.getFlags() & IJavaElementDelta.F_CONTENT) == 0
                    && (delta.getFlags() & IJavaElementDelta.F_CHILDREN) != 0) //A type may have been added or removed.
                processChildren(element, delta);
            break;
        }
        case IJavaElementDelta.REMOVED:
        case IJavaElementDelta.ADDED:
            if (!element.isWorkingCopy())
                disAssociateSourcePlusInner(getFullNameFromElement(element));
            break;
        }
    }

    /**
     * Handle the change for a single element, children will be handled separately.
     *
     */
    protected void processJavaElementChanged(IJavaProject element, IJavaElementDelta delta) {
        if (isInClasspath(element)) {
            if (delta.getKind() == IJavaElementDelta.REMOVED || (delta.getKind() == IJavaElementDelta.CHANGED
                    && (delta.getFlags() & IJavaElementDelta.F_CLOSED) != 0)) {
                if (element.equals(getAdapterFactory().getJavaProject()))
                    stopSynchronizer();
                else
                    flushAll(); //another dependent project has changed so flush all to be safe
                return;
            } else if (delta.getKind() == IJavaElementDelta.ADDED || isClasspathResourceChange(delta)) {
                flushAll();
                return;
            }
            processChildren(element, delta);
        }
    }

    /**
     * Handle the change for a single element, children will be handled separately.
     */
    protected void processJavaElementChanged(IClassFile element, IJavaElementDelta delta) {
        int kind = delta.getKind();
        if (kind == IJavaElementDelta.REMOVED || kind == IJavaElementDelta.ADDED) {
            // It doesn't matter if totally removed or just moved somewhere else, we will clear out and remove the
            // adapter because there could be a rename which would be a different class.
            // Currently the element is already deleted and there is no way to find the types in the unit to remove.
            // So instead we ask factory to remove all it any that start with it plus for inner classes.
            disAssociateSourcePlusInner(getFullNameFromElement(element));
            return; // Since the classfile was removed we don't need to process the children (actually the children list will be empty
        }
        IJavaElementDelta[] children = delta.getAffectedChildren();
        for (int ii = 0; ii < children.length; ii++) {
            processDelta(children[ii]);
        }
    }

    /**
     * Handle the change for a single element, children will be handled separately.
     *
     */
    protected void processJavaElementChanged(IPackageFragmentRoot element, IJavaElementDelta delta) {
        if (flushedAll)
            return;
        if (isClassPathChange(delta))
            flushAll();
        else
            super.processJavaElementChanged(element, delta);
    }

    /* 
     * We will force the flushing of all adaptors for the given package name.
     * This is necessary if a type was reflected prior to the package existing or
     * if the package is deleted.
     * @see org.eclipse.jem.internal.adapters.jdom.JavaModelListener#processJavaElementChanged(org.eclipse.jdt.core.IPackageFragment, org.eclipse.jdt.core.IJavaElementDelta)
     */
    protected void processJavaElementChanged(IPackageFragment element, IJavaElementDelta delta) {
        switch (delta.getKind()) {
        case IJavaElementDelta.ADDED:
            // Even though added there is possibility that package exists in other root but this
            // one may now take priority, so we will clear the package anyway.
            flushPackage(delta.getElement().getElementName(), false);
            break;
        case IJavaElementDelta.REMOVED:
            getAdapterFactory().flushPackage(delta.getElement().getElementName(), false);
            break;
        default:
            super.processJavaElementChanged(element, delta);
        }
    }

    /**
     * Handle the change for a single element, children will be handled separately.
     *
     */
    protected void processJavaElementChanged(IType element, IJavaElementDelta delta) {
        int kind = delta.getKind();
        if (kind == IJavaElementDelta.REMOVED || kind == IJavaElementDelta.ADDED) {
            disAssociateSourcePlusInner(element.getFullyQualifiedName());
        } else {
            flush(element);
            processChildren(element, delta);
            // Note, if a method element or a field was changed, there may be delta.getAffectedChildren()
            //       that will have to be processed if we are to update the JavaMethod/JavaField JDOMAdaptor s.   
        }
    }

    /**
     * Given that an IType does not exists anymore, assume
     * that the type's name is package.filename (without the .java)
     * If we are wrong (if, then a rare case),  we will flush.
     * Next access will induce a reflection attempt.
     * @deprecated This doesn't look like it is ever called. It someone else calls it, please contact development to see if right method to be called.
     */
    protected void processRemoveOrAdd(ICompilationUnit element) {
        disAssociateSource(getFullNameFromElement(element));
    }

    protected String getFullNameFromElement(ICompilationUnit cu) {
        IType primary = cu.findPrimaryType();
        if (primary != null)
            return primary.getFullyQualifiedName();
        else {
            String filename = cu.getElementName();
            // Just strip off extension. There is actually a more complicated test for "java like extenstions" but JDT has that hidden\
            // so we will just guess and take off the extension.
            int idx = filename.lastIndexOf('.');
            if (idx != -1)
                filename = filename.substring(0, idx);
            return ((IPackageFragment) cu.getParent()).getElementName() + '.' + filename;
        }
    }

    protected String getFullNameFromElement(IClassFile cf) {

        return cf.getType().getFullyQualifiedName();

    }

    protected String getFullNameFromElement(IJavaElement element) {
        // TODO: may want to throw IllegalArgumentException here? 
        // if (element == null) throw new IllegalArgumentException("Java Element parameter may not be null");
        String name = element.getElementName();
        if (name.length() <= 5 || !name.substring(name.length() - 5).equals(".java")) { //$NON-NLS-1$
            // Should not be here, 
            Logger logger = JavaPlugin.getDefault().getLogger();
            if (logger.isLoggingLevel(Level.FINE))
                logger.log("Invalid .java file: " + name, Level.FINE); //$NON-NLS-1$
            // Make a guess, at worst case, nothing will come out of this.
            int index = name.lastIndexOf("."); //$NON-NLS-1$
            if (index >= 0)
                name = name.substring(0, index) + ".java"; // rename the extension to .java //$NON-NLS-1$
            else
                name = name + ".java"; //$NON-NLS-1$
        }
        if (element.getParent().getElementName() == null || element.getParent().getElementName().length() == 0)
            return name.substring(0, name.length() - 5);
        else
            return element.getParent().getElementName() + "." + name.substring(0, name.length() - 5); //$NON-NLS-1$
    }

    /**
     * Stop the synchronizer from listening to any more changes.
     */
    public void stopSynchronizer() {
        JavaCore.removeElementChangedListener(this);
    }

    /**
     * @see org.eclipse.jem.internal.adapters.jdom.JavaModelListener#elementChanged(ElementChangedEvent)
     */
    public void elementChanged(ElementChangedEvent event) {
        try {
            flushTypes.clear();
            flushTypePlusInner.clear();
            notifications.clear();
            super.elementChanged(event);
            flushTypes();
            processNotifications();
        } finally {
            flushedAll = false;
            flushTypes.clear();
            flushTypePlusInner.clear();
            notifications.clear();
        }
    }

    /**
     * 
     */
    private void flushTypes() {
        if (!flushTypes.isEmpty()) {
            IType type = null;
            Notification not;
            for (int i = 0; i < flushTypes.size(); i++) {
                type = (IType) flushTypes.get(i);
                not = doFlush(type);
                if (not != null)
                    notifications.add(not);
            }
        }
        if (!flushTypePlusInner.isEmpty()) {
            ICompilationUnit unit = null;
            Notification not;
            for (int i = 0; i < flushTypePlusInner.size(); i++) {
                unit = (ICompilationUnit) flushTypePlusInner.get(i);
                not = doFlush(unit);
                if (not != null)
                    notifications.add(not);
            }
        }
    }

    /**
     * @param notifications
     */
    private void processNotifications() {
        Notifier notifier;
        Notification not;
        for (int i = 0; i < notifications.size(); i++) {
            not = (Notification) notifications.get(i);
            notifier = (Notifier) not.getNotifier();
            if (notifier != null)
                try {
                    notifier.eNotify(not);
                } catch (Exception e) {
                    JavaPlugin.getDefault().getLogger().log(e); //catch exceptions so all notifications are processed
                }
        }
    }

    protected void disAssociateSource(String qualifiedName) {
        Notification not = getAdapterFactory().disAssociateSource(qualifiedName, false);
        if (not != null)
            notifications.add(not);
    }

    protected void disAssociateSourcePlusInner(String qualifiedName) {
        Notification not = getAdapterFactory().disAssociateSourcePlusInner(qualifiedName, false);
        if (not != null)
            notifications.add(not);
    }

    protected void flushAll() {
        notifications.addAll(getAdapterFactory().flushAllNoNotification());
        flushedAll = true;
    }

    /**
     * @see org.eclipse.jem.internal.adapters.jdom.JavaModelListener#processChildren(IJavaElement, IJavaElementDelta)
     */
    protected void processChildren(IJavaElement element, IJavaElementDelta delta) {
        if (!flushedAll)
            super.processChildren(element, delta);
    }

    /**
     * @see org.eclipse.jem.internal.adapters.jdom.JavaModelListener#processDelta(IJavaElementDelta)
     */
    public void processDelta(IJavaElementDelta delta) {
        if (!flushedAll)
            super.processDelta(delta);
    }
}