/**
* <p>Copyright: Copyright (c) 2009</p>
* <p>Company: �������ӹɷ�����˾</p>
*/
package com.hundsun.ares.studio.internal.core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import com.hundsun.ares.studio.core.ARESCore;
import com.hundsun.ares.studio.core.ARESElementChangedEvent;
import com.hundsun.ares.studio.core.ARESModelException;
import com.hundsun.ares.studio.core.IARESElement;
import com.hundsun.ares.studio.core.IARESElementChangeListener;
import com.hundsun.ares.studio.core.IARESModel;
import com.hundsun.ares.studio.core.IARESProject;
import com.hundsun.ares.studio.core.IOpenable;
import com.hundsun.ares.studio.core.IResPathEntry;
/**
* Ĭ�ϵ�DeltaProcessor
* @author sundl
*/
public class DeltaProcessor implements IResourceChangeListener{
private static final Logger logger = Logger.getLogger(DeltaProcessor.class);
List<IARESElementChangeListener> listeners = new ArrayList<IARESElementChangeListener>();
// ��¼ij����Ŀ��������Դ�������ɾ������ʱ����
Map<IProject, Boolean> libChangeState = new HashMap<IProject, Boolean>();
public void resourceChanged(IResourceChangeEvent event) {
logger.debug("Delta processing begin...");
// ����log���κ��쳣�������
try {
logEvent(event);
} catch (Exception e) {
e.printStackTrace();
}
// ���״̬
libChangeState.clear();
IResource resource = event.getResource();
IResourceDelta delta = event.getDelta();
switch (event.getType()) {
case IResourceChangeEvent.PRE_DELETE:
case IResourceChangeEvent.PRE_CLOSE:
// project-delete event should be processed before the underlying resource is really deleted.
if (resource.getType() == IResource.PROJECT ) {
projectPreDelete(event);
}
return;
case IResourceChangeEvent.POST_CHANGE:
checkProjectChanges(delta);
processResourceDelta(delta);
break;
}
// TODO �Ż���ijЩʱ����Ҫ����֪ͨ�¼�
fireChangeEvent(new ARESElementChangedEvent());
logger.debug("Delta processing end...");
}
private void checkProjectChanges(IResourceDelta delta) {
IResource resource = delta.getResource();
IResourceDelta[] children = null;
switch (resource.getType()) {
case IResource.ROOT :
children = delta.getAffectedChildren();
break;
case IResource.PROJECT :
IProject project = (IProject)resource;
IARESProject aresProject = ARESCore.create(project);
//if (aresProject.exists()) {
switch (delta.getKind()) {
case IResourceDelta.CHANGED:
// 2012-03-29 sundl �ֿ�����OPEN��Description�¼��������滻exist����Ϊ����hasNature
if ((delta.getFlags() & IResourceDelta.OPEN) != 0 ) {
// OPEN-CLOSE�����
if (project.isOpen() && ARESProject.hasARESNature(project)) {
addToParentInfo(aresProject);
} else {
try {
aresProject.close();
} catch (ARESModelException e) {
// project doesn't exist or is not open: ignore
}
removeFromParentInfo(aresProject);
}
} else if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) {
// �������ɾ��ARES Nature�����
boolean wasARESProject = wasARESProject(project);
boolean isARESPRoject = ARESProject.hasARESNature(project);
if (wasARESProject != isARESPRoject) {
if (isARESPRoject) {
addToParentInfo(aresProject);
} else {
try {
aresProject.close();
} catch (ARESModelException e) {
// project doesn't exist or is not open: ignore
}
removeFromParentInfo(aresProject);
}
}
}
}
break;
}
if (children != null) {
for (int i = 0; i < children.length; i++) {
checkProjectChanges(children[i]);
}
}
}
private boolean wasARESProject(IProject project) {
try {
IARESProject[] aresProjects = ARESModelManager.getManager().getModel().getARESProjects();
for (IARESProject aresProj : aresProjects) {
if (project.equals(aresProj.getProject()))
return true;
}
} catch (ARESModelException e) {
e.printStackTrace();
}
return false;
}
protected void projectPreDelete(IResourceChangeEvent event) {
try {
IResource resource = event.getResource();
// �����ж���Ŀ����,���ƻ���Ҫ�ģ���Ϊexists�����IJ�ȷ���ԣ�������Ҫֱ��project.hasNature
IARESProject ap = ARESCore.create((IProject)resource);
if (ap != null && ap.exists()) {
ap.close();
removeFromParentInfo(ap);
}
} catch (CoreException e) {
// project doesn't exist or is not open: ignore
}
}
protected void processResourceDelta(IResourceDelta changes) {
try {
IARESModel model = ARESCore.getModel();
if (!model.isOpen()) {
model.open(null);
}
IResourceDelta[] deltas = changes.getAffectedChildren(IResourceDelta.ADDED | IResourceDelta.REMOVED | IResourceDelta.CHANGED);
for (IResourceDelta delta : deltas) {
IResource res = delta.getResource();
IProject project = (IProject)res;
// ���е���Ŀ��Ҫ��һ��ARES Nature
if (ARESProject.hasARESNature(project)) {
traverseDelta(delta);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
protected void traverseDelta(IResourceDelta delta) {
boolean processChildren = updateCurrentDelta(delta);
if (processChildren) {
IResourceDelta[] children = delta.getAffectedChildren();
for (IResourceDelta child : children) {
traverseDelta(child);
}
}
}
protected boolean updateCurrentDelta(IResourceDelta delta) {
logger.trace("update delta: " + toDebugString(delta));
IResource resource = delta.getResource();
// 2012-2-28 sundl ���ӵ���respath provider�ж��Ƿ���respath�ı仯��
final IProject project = resource.getProject();
// �ȼ��.respath�ļ����ֿ�����respath�ļ��������ﴦ��
if (resource.getProjectRelativePath().equals(new Path(ARESProject.RES_PATH_FILE))) {
logger.trace(String.format(".respath file in project \"%s\" changed!", project.getName()));
updateResPath(ARESCore.create(project));
return false;
}
// boolean respathChanged = false;
// // ���respath�ļ�û�з����仯���ٵ���respath provider
// RespathProviderRegistry reg = RespathProviderRegistry.getInstance();
// for (IRespathProviderDescriptor provider : reg.getProviders()) {
// IRespathProvider rpProvider = provider.getProvider();
// if (rpProvider.containsRespathChange(delta)) {
// respathChanged = true;
// logger.trace(String.format("respath provider \"%s\" reports that respath changed!", provider.getName()));
// break;
// }
// }
// if (respathChanged) {
// Job job = new Job("") {
// @Override
// protected IStatus run(IProgressMonitor monitor) {
// try {
// IARESProject aresProj = ARESCore.create(project);
// IResPathEntry[] oldPath = aresProj.getRawResPath();
// IResPathEntry[] newPath = ((ARESProject) aresProj).readResPath();
// resPathChanged(aresProj, oldPath, newPath, false);
// libChangeState.put(project, true);
// } catch (ARESModelException e) {
// e.printStackTrace();
// }
// fireChangeEvent();
// return Status.OK_STATUS;
// }
// };
// job.schedule();
// return false;
// }
// if (respathChanged) {
// try {
// IARESProject aresProj = ARESCore.create(project);
// IResPathEntry[] oldPath = aresProj.getRawResPath();
// IResPathEntry[] newPath = ((ARESProject) aresProj).readResPath();
// resPathChanged(aresProj, oldPath, newPath, false);
// libChangeState.put(project, true);
// } catch (ARESModelException e) {
// e.printStackTrace();
// }
// return false;
// }
IARESElement element = ARESCore.create(resource);
if (element == null)
return false;
switch(delta.getKind()) {
case IResourceDelta.ADDED:
elementAdded(element, delta);
logger.trace("update delta: element added");
return element.getElementType() == IARESElement.COMMON_MODULE;
case IResourceDelta.REMOVED:
elementRemoved(element, delta);
logger.trace("update delta: element removed");
// ����ModuleҪ����������Ϊ���ڴ��У�ģ�鶼��ƽ���ġ�
return element.getElementType() == IARESElement.COMMON_MODULE;
case IResourceDelta.CHANGED:
logger.trace("update delta: element changed");
int flags = delta.getFlags();
if ((flags & IResourceDelta.CONTENT) != 0) {
if (element instanceof IOpenable) {
try {
((IOpenable) element).close();
logger.trace("update delta: element closed...");
} catch (ARESModelException e) {
}
}
}
return true;
}
return true;
}
/**
* ��չ��MavenProvider���ڱ仯�ļ������첽ģʽ������������Ҫ����һ����ڹ�����ã���������������pom�仯�Ժ�ˢ��respath.
* @param project
*/
public void updateResPath(IARESProject project) {
try {
IResPathEntry[] oldPath = project.getRawResPath();
IResPathEntry[] newPath = ((ARESProject) project).readResPath();
resPathChanged(project, oldPath, newPath, true);
libChangeState.put(project.getProject(), true);
} catch (ARESModelException e) {
e.printStackTrace();
}
}
public boolean hasLibPathChanged(IProject project) {
Boolean changed = libChangeState.get(project);
if (changed != null) {
return changed;
}
return false;
}
private void elementAdded(IARESElement element, IResourceDelta delta) {
try {
if ((delta.getFlags() & IResourceDelta.MOVED_FROM) == 0) {
addToParentInfo(element);
if (element instanceof IOpenable) {
((IOpenable) element).close();
}
} else {
addToParentInfo(element);
}
} catch (ARESModelException e) {
e.printStackTrace();
}
}
protected void elementRemoved(IARESElement element, IResourceDelta delta) {
if (element instanceof IOpenable) {
try {
((IOpenable)element).close();
} catch (ARESModelException e) {
e.printStackTrace();
}
}
removeFromParentInfo(element);
}
protected void addToParentInfo(IARESElement child) {
Openable parent = (Openable)child.getParent();
if(parent != null && parent.isOpen()) {
ARESElementInfo info;
try {
info = (ARESElementInfo)parent.getElementInfo();
info.addChild(child);
} catch (ARESModelException e) {
e.printStackTrace();
}
}
}
/*
* 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,
* this has no effect.
*/
protected void removeFromParentInfo(IARESElement element) {
IARESElement parent = element.getParent();
if (parent instanceof IOpenable) {
Openable openable = (Openable)parent;
if (openable.isOpen()) {
try {
OpenableElementInfo info = (OpenableElementInfo) openable.getElementInfo();
info.removeChild(element);
} catch (ARESModelException e) {
e.printStackTrace();
}
}
}
}
// 2012-03-08 sundl ���Ӳ����������ü�����ȷ����������
protected void fireChangeEvent(ARESElementChangedEvent event) {
try {
for (IARESElementChangeListener listener : listeners) {
listener.elementChanged(event);
}
} catch (Exception e) {
logger.warn("", e);
// TODO: handle exception
}
}
public void entriesAdded(IARESProject project, IResPathEntry[] entries) {
for (IResPathEntry entry : entries) {
IOpenable child = null;
if (entry.getEntryKind() == IResPathEntry.RPE_SOURCE) {
child = project.getModuleRoot(entry);
} else if (entry.getEntryKind() == IResPathEntry.RPE_LIBRAY){
child = project.getReferencedLibrary(entry);
}
if (child == null)
continue;
addToParentInfo((IARESElement)child);
}
fireChangeEvent(new ARESElementChangedEvent());
}
public void entriesRemoved(IARESProject project, IResPathEntry[] entries) {
for (IResPathEntry entry : entries) {
IOpenable child = null;
if (entry.getEntryKind() == IResPathEntry.RPE_SOURCE) {
child = project.getModuleRoot(entry);
} else if (entry.getEntryKind() == IResPathEntry.RPE_LIBRAY){
child = project.getReferencedLibrary(entry);
}
try {
if (child != null) {
child.close();
removeFromParentInfo((IARESElement)child);
}
} catch (ARESModelException e) {
e.printStackTrace();
}
}
fireChangeEvent(new ARESElementChangedEvent());
}
public void resPathChanged(IARESProject project, IResPathEntry[] oldPath, IResPathEntry[] newPath, boolean fireEvent) {
List<IResPathEntry> deletedEntries = new ArrayList<IResPathEntry>();
List<IResPathEntry> addedEntries = new ArrayList<IResPathEntry>();
for (IResPathEntry entry : newPath) {
if (!ArrayUtils.contains(oldPath, entry)) {
addedEntries.add(entry);
}
}
for (IResPathEntry entry : oldPath) {
if (!ArrayUtils.contains(newPath, entry)) {
deletedEntries.add(entry);
}
}
for (IResPathEntry entry : addedEntries) {
IOpenable child = null;
if (entry.getEntryKind() == IResPathEntry.RPE_SOURCE) {
child = project.getModuleRoot(entry);
} else if (entry.getEntryKind() == IResPathEntry.RPE_LIBRAY){
child = project.getReferencedLibrary(entry);
}
if (child != null)
addToParentInfo((IARESElement)child);
}
for (IResPathEntry entry : deletedEntries) {
IOpenable child = null;
if (entry.getEntryKind() == IResPathEntry.RPE_SOURCE) {
child = project.getModuleRoot(entry);
} else if (entry.getEntryKind() == IResPathEntry.RPE_LIBRAY){
child = project.getReferencedLibrary(entry);
}
try {
if (child != null) {
child.close();
removeFromParentInfo((IARESElement)child);
}
} catch (ARESModelException e) {
e.printStackTrace();
}
}
try {
AresProjectInfo projectInfo = (AresProjectInfo) ((ARESProject)project).getElementInfo();
projectInfo.resPath = newPath;
} catch (ARESModelException e) {
e.printStackTrace();
}
// 2012-03-08 sundl ���Ӳ������¼�ȷ��Ϊrespath�仯�¼���ʵ�ֿ��Լ����Ĺ���
if (fireEvent)
fireChangeEvent(new ARESElementChangedEvent(project, ARESElementChangedEvent.RES_PATH));
}
private void logEvent(IResourceChangeEvent event) {
logger.trace("Event kind: " + event.getBuildKind());
IResourceDelta delta = event.getDelta();
if (delta != null)
logger.trace("Delta: " + toDeepDebugString(delta));
}
/**
* Returns a string representation of this delta's
* immediate structure suitable for debug purposes.
*/
public String toDebugString(IResourceDelta delta) {
final StringBuffer buffer = new StringBuffer();
writeDebugString(buffer, delta);
return buffer.toString();
}
/**
* Returns a string representation of this delta's
* deep structure suitable for debug purposes.
*/
public String toDeepDebugString(IResourceDelta delta) {
final StringBuffer buffer = new StringBuffer("\n"); //$NON-NLS-1$
writeDebugString(buffer, delta);
IResourceDelta[] children = delta.getAffectedChildren();
for (int i = 0; i < children.length; ++i)
buffer.append(toDeepDebugString(children[i]));
return buffer.toString();
}
/**
* Writes a string representation of this delta's
* immediate structure on the given string buffer.
*/
public void writeDebugString(StringBuffer buffer, IResourceDelta delta) {
buffer.append(delta.getFullPath());
buffer.append('[');
switch (delta.getKind()) {
case IResourceDelta.ADDED :
buffer.append('+');
break;
case IResourceDelta.ADDED_PHANTOM :
buffer.append('>');
break;
case IResourceDelta.REMOVED :
buffer.append('-');
break;
case IResourceDelta.REMOVED_PHANTOM :
buffer.append('<');
break;
case IResourceDelta.CHANGED :
buffer.append('*');
break;
case IResourceDelta.NO_CHANGE :
buffer.append('~');
break;
default :
buffer.append('?');
break;
}
buffer.append("]: {"); //$NON-NLS-1$
int changeFlags = delta.getFlags();
boolean prev = false;
if ((changeFlags & IResourceDelta.CONTENT) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("CONTENT"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & IResourceDelta.LOCAL_CHANGED) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("LOCAL_CHANGED"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & IResourceDelta.MOVED_FROM) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("MOVED_FROM(" + delta.getMovedFromPath() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
prev = true;
}
if ((changeFlags & IResourceDelta.MOVED_TO) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("MOVED_TO(" + delta.getMovedToPath() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
prev = true;
}
if ((changeFlags & IResourceDelta.OPEN) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("OPEN"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & IResourceDelta.TYPE) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("TYPE"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & IResourceDelta.SYNC) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("SYNC"); //$NON-NLS-1$
prev = true;
}
// if ((changeFlags & IResourceDelta.MARKERS) != 0) {
// if (prev)
// buffer.append(" | "); //$NON-NLS-1$
// buffer.append("MARKERS"); //$NON-NLS-1$
//// writeMarkerDebugString(buffer);
// prev = true;
// }
if ((changeFlags & IResourceDelta.REPLACED) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("REPLACED"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & IResourceDelta.DESCRIPTION) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("DESCRIPTION"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & IResourceDelta.ENCODING) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("ENCODING"); //$NON-NLS-1$
prev = true;
}
buffer.append("}"); //$NON-NLS-1$
// if (delta.isTeamPrivate())
// buffer.append(" (team private)"); //$NON-NLS-1$
// if (delta.isHidden())
// buffer.append(" (hidden)"); //$NON-NLS-1$
}
// public void writeMarkerDebugString(StringBuffer buffer, IResourceDelta delta) {
// Map markerDeltas = deltaInfo.getMarkerDeltas();
// if (markerDeltas == null || markerDeltas.isEmpty())
// return;
// buffer.append('[');
// for (Iterator e = markerDeltas.keySet().iterator(); e.hasNext();) {
// IPath key = (IPath) e.next();
// if (getResource().getFullPath().equals(key)) {
// IMarkerSetElement[] deltas = ((MarkerSet) markerDeltas.get(key)).elements();
// boolean addComma = false;
// for (int i = 0; i < deltas.length; i++) {
// IMarkerDelta delta = (IMarkerDelta) deltas[i];
// if (addComma)
// buffer.append(',');
// switch (delta.getKind()) {
// case IResourceDelta.ADDED :
// buffer.append('+');
// break;
// case IResourceDelta.REMOVED :
// buffer.append('-');
// break;
// case IResourceDelta.CHANGED :
// buffer.append('*');
// break;
// }
// buffer.append(delta.getId());
// addComma = true;
// }
// }
// }
// buffer.append(']');
// }
}