blob: 095250eba7c841e9ba06ad4d993cd3c3559d841f [file] [log] [blame]
/*******************************************************************************
* 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;
/*
* $RCSfile: JavaReflectionSynchronizer.java,v $
* $Revision: 1.15 $ $Date: 2006/05/17 20:13:58 $
*/
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, true));
}
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 : {
if (delta.getAffectedChildren().length == 0)
flushPackage(delta.getElement().getElementName(), true);
break;
}
case IJavaElementDelta.REMOVED :{
if (delta.getAffectedChildren().length == 0)
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) {
try {
return cf.getType().getFullyQualifiedName();
} catch (JavaModelException e) {
JavaPlugin.getDefault().getLogger().log(e, Level.WARNING);
String cfName = cf.getElementName();
return cfName.substring(0, cfName.length()-(".class".length()));
}
}
protected String getFullNameFromElement(IJavaElement element) {
String name = element.getElementName();
if (element == null || 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);
}
}