import java.io.File;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
import net.sourceforge.phpdt.core.ElementChangedEvent;
import net.sourceforge.phpdt.core.IClasspathEntry;
+import net.sourceforge.phpdt.core.IElementChangedListener;
import net.sourceforge.phpdt.core.IJavaElement;
+import net.sourceforge.phpdt.core.IJavaElementDelta;
import net.sourceforge.phpdt.core.IJavaModel;
import net.sourceforge.phpdt.core.IJavaProject;
+import net.sourceforge.phpdt.core.JavaCore;
import net.sourceforge.phpdt.core.JavaModelException;
import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
-import net.sourceforge.phpeclipse.PHPCore;
import net.sourceforge.phpeclipse.PHPeclipsePlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
+import net.sourceforge.phpdt.internal.core.builder.PHPBuilder;
+
+import net.sourceforge.phpdt.internal.core.DeltaProcessingState;
+import net.sourceforge.phpdt.internal.core.JavaElementDelta;
+import net.sourceforge.phpdt.internal.core.JavaModelManager;
+import net.sourceforge.phpdt.internal.core.util.Util;
/**
final static String EXTERNAL_JAR_UNCHANGED = "external jar unchanged"; //$NON-NLS-1$
final static String INTERNAL_JAR_IGNORE = "internal jar ignore"; //$NON-NLS-1$
- final static int NON_JAVA_RESOURCE = -1;
+ private final static int NON_JAVA_RESOURCE = -1;
+ public static boolean DEBUG = false;
+ public static boolean VERBOSE = false;
+ public static final int DEFAULT_CHANGE_EVENT = 0; // must not collide with ElementChangedEvent event masks
/**
* The <code>JavaElementDelta</code> corresponding to the <code>IResourceDelta</code> being translated.
*/
* This is used as a stack of java elements (using getParent() to pop it, and
* using the various get*(...) to push it. */
Openable currentElement;
-
+ /*
+ * Queue of deltas created explicily by the Java Model that
+ * have yet to be fired.
+ */
+ public ArrayList javaModelDeltas= new ArrayList();
+ /*
+ * Queue of reconcile deltas on working copies that have yet to be fired.
+ * This is a table form IWorkingCopy to IJavaElementDelta
+ */
+ public HashMap reconcileDeltas = new HashMap();
+
+ /*
+ * Turns delta firing on/off. By default it is on.
+ */
+ private boolean isFiring= true;
+
+
public HashMap externalTimeStamps = new HashMap();
public HashSet projectsToUpdate = new HashSet();
// list of root projects which namelookup caches need to be updated for dependents
// TODO: (jerome) is it needed? projectsToUpdate might be sufficient
public HashSet projectsForDependentNamelookupRefresh = new HashSet();
- JavaModelManager manager;
+ /*
+ * The global state of delta processing.
+ */
+ private DeltaProcessingState state;
+
+ /*
+ * The Java model manager
+ */
+ private JavaModelManager manager;
/* A table from IJavaProject to an array of IPackageFragmentRoot.
* This table contains the pkg fragment roots of the project that are being deleted.
* This is null if no refresh is needed.
*/
HashSet refreshedElements;
- public static boolean VERBOSE = false;
class OutputsInfo {
IPath[] paths;
}
}
- DeltaProcessor(JavaModelManager manager) {
+// DeltaProcessor(JavaModelManager manager) {
+// this.manager = manager;
+// }
+
+ /*
+ * Type of event that should be processed no matter what the real event type is.
+ */
+ public int overridenEventType = -1;
+
+ public DeltaProcessor(DeltaProcessingState state, JavaModelManager manager) {
+ this.state = state;
this.manager = manager;
}
-
/*
* Adds the dependents of the given project to the list of the projects
* to update.
* to update.
*/
void addToProjectsToUpdateWithDependents(IProject project) {
- this.projectsToUpdate.add(PHPCore.create(project));
+ this.projectsToUpdate.add(JavaCore.create(project));
// this.addDependentProjects(project.getFullPath(), this.projectsToUpdate);
}
// source attachment path
if (entry.getEntryKind() != IClasspathEntry.CPE_LIBRARY) continue;
- QualifiedName qName = new QualifiedName(PHPCore.PLUGIN_ID, "sourceattachment: " + path.toOSString()); //$NON-NLS-1$;
+ QualifiedName qName = new QualifiedName(JavaCore.PLUGIN_ID, "sourceattachment: " + path.toOSString()); //$NON-NLS-1$;
String propertyString = null;
try {
propertyString = ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(qName);
}
return false;
}
-
+ /*
+ * Merges all awaiting deltas.
+ */
+ private IJavaElementDelta mergeDeltas(Collection deltas) {
+ if (deltas.size() == 0) return null;
+ if (deltas.size() == 1) return (IJavaElementDelta)deltas.iterator().next();
+
+ if (VERBOSE) {
+ System.out.println("MERGING " + deltas.size() + " DELTAS ["+Thread.currentThread()+"]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ Iterator iterator = deltas.iterator();
+ JavaElementDelta rootDelta = new JavaElementDelta(this.manager.javaModel);
+ boolean insertedTree = false;
+ while (iterator.hasNext()) {
+ JavaElementDelta delta = (JavaElementDelta)iterator.next();
+ if (VERBOSE) {
+ System.out.println(delta.toString());
+ }
+ IJavaElement element = delta.getElement();
+ if (this.manager.javaModel.equals(element)) {
+ IJavaElementDelta[] children = delta.getAffectedChildren();
+ for (int j = 0; j < children.length; j++) {
+ JavaElementDelta projectDelta = (JavaElementDelta) children[j];
+ rootDelta.insertDeltaTree(projectDelta.getElement(), projectDelta);
+ insertedTree = true;
+ }
+ IResourceDelta[] resourceDeltas = delta.getResourceDeltas();
+ if (resourceDeltas != null) {
+ for (int i = 0, length = resourceDeltas.length; i < length; i++) {
+ rootDelta.addResourceDelta(resourceDeltas[i]);
+ insertedTree = true;
+ }
+ }
+ } else {
+ rootDelta.insertDeltaTree(element, delta);
+ insertedTree = true;
+ }
+ }
+ if (insertedTree) return rootDelta;
+ return null;
+ }
+ private void notifyListeners(IJavaElementDelta deltaToNotify, int eventType, IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) {
+ final ElementChangedEvent extraEvent = new ElementChangedEvent(deltaToNotify, eventType);
+ for (int i= 0; i < listenerCount; i++) {
+ if ((listenerMask[i] & eventType) != 0){
+ final IElementChangedListener listener = listeners[i];
+ long start = -1;
+ if (VERBOSE) {
+ System.out.print("Listener #" + (i+1) + "=" + listener.toString());//$NON-NLS-1$//$NON-NLS-2$
+ start = System.currentTimeMillis();
+ }
+ // wrap callbacks with Safe runnable for subsequent listeners to be called when some are causing grief
+ Platform.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ Util.log(exception, "Exception occurred in listener of Java element change notification"); //$NON-NLS-1$
+ }
+ public void run() throws Exception {
+ listener.elementChanged(extraEvent);
+ }
+ });
+ if (VERBOSE) {
+ System.out.println(" -> " + (System.currentTimeMillis()-start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ }
+ }
/**
* Generic processing for elements with changed contents:<ul>
* <li>The element is closed such that any subsequent accesses will re-open
// //fCurrentDelta.changed(project, IJavaElementDelta.F_OPTIONS_CHANGED);
// }
// }
-
+ /*
+ * Registers the given delta with this delta processor.
+ */
+ public void registerJavaModelDelta(IJavaElementDelta delta) {
+ this.javaModelDeltas.add(delta);
+ }
/**
* Removes the given element from its parents cache of children. If the
* element does not have a parent, or the parent is not currently open,
}
}
}
+
/**
* Notification that some resource changes have happened
* on the platform, and that the Java Model should update any required
* @see IResource
*/
public void resourceChanged(IResourceChangeEvent event) {
-
+ // jsurfer TODO compare 3.0 sources
if (event.getSource() instanceof IWorkspace) {
+ int eventType = this.overridenEventType == -1 ? event.getType() : this.overridenEventType;
IResource resource = event.getResource();
IResourceDelta delta = event.getDelta();
- switch(event.getType()){
+ switch(eventType){
case IResourceChangeEvent.PRE_DELETE :
try {
if(resource.getType() == IResource.PROJECT
&& ((IProject) resource).hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) {
- // TODO khartlage temp-del
+ // TODO jsurfer temp-del
// this.deleting((IProject)resource);
}
} catch(CoreException e){
return;
case IResourceChangeEvent.PRE_AUTO_BUILD :
-// TODO khartlage temp-del
+// TODO jsurfer temp-del
// if(isAffectedBy(delta)) { // avoid populating for SYNC or MARKER deltas
// this.checkProjectsBeingAddedOrRemoved(delta);
//
break;
case IResourceChangeEvent.POST_AUTO_BUILD :
-// TODO khartlage temp-del
+// TODO jsurfer temp-del
// JavaBuilder.finishedBuilding(event);
break;
case IResourceChangeEvent.POST_CHANGE :
-// TODO khartlage temp-del
+// TODO jsurfer temp-del
// if (isAffectedBy(delta)) {
// try {
// if (this.refreshedElements != null) {
}
}
/*
+ * Flushes all deltas without firing them.
+ */
+ public void flush() {
+ this.javaModelDeltas = new ArrayList();
+ }
+ /*
* Finds the root info this path is included in.
* Returns null if not found.
*/
return null;
}
/*
+ * Fire Java Model delta, flushing them after the fact after post_change notification.
+ * If the firing mode has been turned off, this has no effect.
+ */
+ public void fire(IJavaElementDelta customDelta, int eventType) {
+ if (!this.isFiring) return;
+
+ if (DEBUG) {
+ System.out.println("-----------------------------------------------------------------------------------------------------------------------");//$NON-NLS-1$
+ }
+
+ IJavaElementDelta deltaToNotify;
+ if (customDelta == null){
+ deltaToNotify = this.mergeDeltas(this.javaModelDeltas);
+ } else {
+ deltaToNotify = customDelta;
+ }
+
+ // Refresh internal scopes
+// if (deltaToNotify != null) {
+// Iterator scopes = this.manager.searchScopes.keySet().iterator();
+// while (scopes.hasNext()) {
+// AbstractSearchScope scope = (AbstractSearchScope)scopes.next();
+// scope.processDelta(deltaToNotify);
+// }
+// }
+
+ // Notification
+
+ // Important: if any listener reacts to notification by updating the listeners list or mask, these lists will
+ // be duplicated, so it is necessary to remember original lists in a variable (since field values may change under us)
+ IElementChangedListener[] listeners = this.state.elementChangedListeners;
+ int[] listenerMask = this.state.elementChangedListenerMasks;
+ int listenerCount = this.state.elementChangedListenerCount;
+
+ switch (eventType) {
+ case DEFAULT_CHANGE_EVENT:
+ firePostChangeDelta(deltaToNotify, listeners, listenerMask, listenerCount);
+ fireReconcileDelta(listeners, listenerMask, listenerCount);
+ break;
+ case ElementChangedEvent.POST_CHANGE:
+ firePostChangeDelta(deltaToNotify, listeners, listenerMask, listenerCount);
+ fireReconcileDelta(listeners, listenerMask, listenerCount);
+ break;
+ }
+ }
+
+ private void firePostChangeDelta(
+ IJavaElementDelta deltaToNotify,
+ IElementChangedListener[] listeners,
+ int[] listenerMask,
+ int listenerCount) {
+
+ // post change deltas
+ if (DEBUG){
+ System.out.println("FIRING POST_CHANGE Delta ["+Thread.currentThread()+"]:"); //$NON-NLS-1$//$NON-NLS-2$
+ System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
+ }
+ if (deltaToNotify != null) {
+ // flush now so as to keep listener reactions to post their own deltas for subsequent iteration
+ this.flush();
+
+ notifyListeners(deltaToNotify, ElementChangedEvent.POST_CHANGE, listeners, listenerMask, listenerCount);
+ }
+ }
+ private void fireReconcileDelta(
+ IElementChangedListener[] listeners,
+ int[] listenerMask,
+ int listenerCount) {
+
+
+ IJavaElementDelta deltaToNotify = mergeDeltas(this.reconcileDeltas.values());
+ if (DEBUG){
+ System.out.println("FIRING POST_RECONCILE Delta ["+Thread.currentThread()+"]:"); //$NON-NLS-1$//$NON-NLS-2$
+ System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
+ }
+ if (deltaToNotify != null) {
+ // flush now so as to keep listener reactions to post their own deltas for subsequent iteration
+ this.reconcileDeltas = new HashMap();
+
+ notifyListeners(deltaToNotify, ElementChangedEvent.POST_RECONCILE, listeners, listenerMask, listenerCount);
+ }
+ }
+ /*
* Returns the root info for the given path. Look in the old roots table if kind is REMOVED.
*/
RootInfo rootInfo(IPath path, int kind) {