blob: f2cd4866eda55c783ac5a6b8f9ea22c811742908 [file] [log] [blame]
package org.eclipse.jem.internal.beaninfo.adapters;
/*******************************************************************************
* Copyright (c) 2001, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
/*
* $RCSfile: BeaninfoModelSynchronizer.java,v $
* $Revision: 1.3 $ $Date: 2004/03/22 23:49:10 $
*/
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.*;
import org.eclipse.jem.internal.adapters.jdom.JavaModelListener;
import org.eclipse.jem.internal.beaninfo.core.*;
/**
* This class listens for changes to the java model and flushs the
* appropriate class introspection.
*/
public class BeaninfoModelSynchronizer extends JavaModelListener {
protected BeaninfoAdapterFactory fAdapterFactory;
protected IJavaProject fProject; // The project this listener is opened on.
protected IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
private static final IPath CLASSPATH_PATH = new Path(".classpath"); //$NON-NLS-1$
private static final IPath BEANINFOCONFIG_PATH = new Path(BeaninfoNature.P_BEANINFO_SEARCH_PATH); //$NON-NLS-1$
public BeaninfoModelSynchronizer(BeaninfoAdapterFactory aFactory, IJavaProject aProject) {
super();
fAdapterFactory = aFactory;
fProject = aProject;
// We're really only interested in Post_Change, not the others, so we will
// remove ourself (since super ctor added ourself) and then add ourself
// back with only post change. (Post change is after everything has been
// reconciled and build).
JavaCore.removeElementChangedListener(this);
JavaCore.addElementChangedListener(this, ElementChangedEvent.POST_CHANGE);
}
/**
* Stop the synchronizer from listening to any more changes.
*/
public void stopSynchronizer(boolean clearResults) {
JavaCore.removeElementChangedListener(this);
getAdapterFactory().closeAll(clearResults);
}
public BeaninfoAdapterFactory getAdapterFactory() {
return fAdapterFactory;
}
protected IJavaProject getJavaProject(IClasspathEntry entry) {
IProject proj = workspaceRoot.getProject(entry.getPath().segment(0));
if (proj != null)
return (IJavaProject) JavaCore.create(proj);
return null;
}
private boolean isClassPathChange(IJavaElementDelta delta) {
int flags = delta.getFlags();
return (
delta.getKind() == IJavaElementDelta.CHANGED
&& ((flags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) != 0)
|| ((flags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0)
|| ((flags & IJavaElementDelta.F_REORDER) != 0));
}
/**
* This method will check to see if a <code>javaProject</code> is a project in the
* classpath of the adapterFactory java project.
*/
protected boolean isInClasspath(IJavaProject javaProject) {
IJavaProject adapterJavaProject = fProject;
if (javaProject.equals(adapterJavaProject))
return true;
return isInClasspath(javaProject, adapterJavaProject, true, new HashSet());
}
protected boolean isInClasspath(IJavaProject testProject, IJavaProject targetProject, boolean isFirstLevel, Set visited) {
if (visited.contains(targetProject))
return false;
visited.add(targetProject);
IClasspathEntry[] entries = null;
try {
entries = targetProject.getRawClasspath();
} catch (JavaModelException e) {
return false;
}
IClasspathEntry entry, resEntry;
IJavaProject proj = null;
List projects = null;
for (int i = 0; i < entries.length; i++) {
entry = entries[i];
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
resEntry = JavaCore.getResolvedClasspathEntry(entry);
proj = getJavaProject(resEntry);
if (isFirstLevel || resEntry.isExported()) {
if (proj.equals(testProject))
return true;
else {
if (projects == null)
projects = new ArrayList();
projects.add(proj);
}
}
}
}
return isInClasspath(testProject, projects, false, visited);
}
protected boolean isInClasspath(IJavaProject testProject, List someJavaProjects, boolean isFirstLevel, Set visited) {
if (someJavaProjects == null)
return false;
int size = someJavaProjects.size();
IJavaProject javaProj = null;
for (int i = 0; i < size; i++) {
javaProj = (IJavaProject) someJavaProjects.get(i);
return isInClasspath(testProject, javaProj, isFirstLevel, visited);
}
return false;
}
/*
* Test if .classpath/.beaninfoconfig is part of the change. This is necessary
* because .classpath/.beaninfoconfig changes of DEPENDENT PROJECTS are not signaled
* through classpath change mechanisms of the top project.
*/
private boolean isClasspathResourceChange(IJavaElementDelta delta) {
IResourceDelta[] resources = delta.getResourceDeltas();
if (resources == null)
return false;
IPath path = null;
for (int i = 0; i < resources.length; i++) {
if (resources[i].getKind() != IResourceDelta.NO_CHANGE) {
path = resources[i].getProjectRelativePath();
if (path.equals(CLASSPATH_PATH) || path.equals(BEANINFOCONFIG_PATH))
return true;
}
}
return false;
}
protected void processJavaElementChanged(IJavaProject element, IJavaElementDelta delta) {
if (isInClasspath(element)) {
if (delta.getKind() == IJavaElementDelta.REMOVED || delta.getKind() == IJavaElementDelta.ADDED) {
// Don't need to do anything for delete/close/add/open of main project because there is much more that needs to
// be done by BeaninfoNature on project close/delete, so nature listens for this and does the appropriate cleanup.
if (!element.equals(fProject)) {
// However, all other projects are required projects and if they are deleted/closed/added/opened when need to do
// a full flush because we don't know any of the state, whether they are still there or not.
getAdapterFactory().markAllStale();
}
return;
} else if (isClasspathResourceChange(delta)) {
getAdapterFactory().markAllStale(); // The .classpath file itself in SOME DEPENDENT PROJECT has changed.
return;
}
processChildren(element, delta);
}
}
/**
* Handle the change for a single element, children will be handled separately.
* If a working copy, then ignore it because we don't care about changes until
* they are committed. Else, if the CU has changed content then mark all of the
* types in this CU (such as inner classes) as stale.
* If it is not a content change then process the children.
*/
protected void processJavaElementChanged(ICompilationUnit element, IJavaElementDelta delta) {
if (!element.isWorkingCopy()) {
if (((delta.getKind() == IJavaElementDelta.CHANGED && (delta.getFlags() & IJavaElementDelta.F_PRIMARY_WORKING_COPY) == 0) || delta.getKind() == IJavaElementDelta.ADDED)) {
try {
IType[] flushTypes = element.getAllTypes();
for (int i = 0; i < flushTypes.length; i++) {
getAdapterFactory().markStaleIntrospection(flushTypes[i].getFullyQualifiedName(), false);
}
} catch (JavaModelException e) {
}
}
if (delta.getKind() == IJavaElementDelta.REMOVED) {
// 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.
getAdapterFactory().unregisterIntrospectionPlusInner(getFullNameFromElement(element));
return;
// Since the compilation unit was removed we don't need to process the children (actually the children list will be empty
}
processChildren(element, delta);
}
}
/**
* Handle the change for a single element, children will be handled separately.
*/
protected void processJavaElementChanged(IClassFile element, IJavaElementDelta delta) {
if (delta.getKind() == IJavaElementDelta.REMOVED) {
// 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.
getAdapterFactory().unregisterIntrospectionPlusInner(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]);
}
}
protected String getFullNameFromElement(IJavaElement element) {
String name = element.getElementName();
if (!(element instanceof ICompilationUnit || element instanceof IClassFile))
return name; // Shouldn't be here
// remove extension.
int periodNdx = name.lastIndexOf('.');
if (periodNdx == -1)
return name; // Shouldn't be here. There should be an extension
String typeName = null;
String parentName = element.getParent().getElementName();
if (parentName == null || parentName.length() == 0)
typeName = name.substring(0, periodNdx); // In default package
else
typeName = parentName + "." + name.substring(0, periodNdx); //$NON-NLS-1$
return typeName;
}
/**
* Handle the change for a single element, children will be handled separately.
* If the classpath has changed, mark all as stale because we don't know what
* has changed. Things that were in the path may no longer be in the path, or
* the order was changed, which could affect the introspection.
*/
protected void processJavaElementChanged(IPackageFragmentRoot element, IJavaElementDelta delta) {
if (isClassPathChange(delta))
fAdapterFactory.markAllStale();
else
super.processJavaElementChanged(element, delta);
}
/**
* Handle the change for a single element, children will be handled separately.
* Something about the type has changed. If it was removed (not a move), then close the
* adapter too.
*/
protected void processJavaElementChanged(IType element, IJavaElementDelta delta) {
if (delta.getKind() == IJavaElementDelta.REMOVED) {
getAdapterFactory().unregisterIntrospectionPlusInner(element.getFullyQualifiedName()); // Close it out. Doesn't matter if moved_to, that would be a rename which requires brand new class.
} else
getAdapterFactory().markStaleIntrospection(element.getFullyQualifiedName(), false); // Just mark it stale
processChildren(element, delta);
}
public String toString() {
return super.toString()+" "+fProject.getElementName(); //$NON-NLS-1$
}
}