/*******************************************************************************
* Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Exadel, Inc. and Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.model.markers;
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.progress.UIJob;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.filesystems.FileSystemsHelper;
import org.jboss.tools.common.model.impl.XModelObjectImpl;
import org.jboss.tools.common.model.plugin.ModelPlugin;
import org.jboss.tools.common.model.util.EclipseResourceUtil;
public class XMarkerManager implements IResourceChangeListener {
private static XMarkerManager instance;
public static XMarkerManager getInstance() {
if(instance == null) {
instance = new XMarkerManager();
}
return instance;
}
private Map<IFile, Set<XMarker>> xmarkers = new HashMap<IFile, Set<XMarker>>();
private Map<IFile, Set<XModelObject>> errorObjects = new HashMap<IFile, Set<XModelObject>>();
private Map<IFile, Set<XModelObject>> warningObjects = new HashMap<IFile, Set<XModelObject>>();
private Set<IFile> uptodate = new HashSet<IFile>();
private XMarkerManager() {
ModelPlugin.getWorkspace().addResourceChangeListener(this);
}
public void resourceChanged(IResourceChangeEvent event) {
if((event.getType() == IResourceChangeEvent.PRE_DELETE
|| event.getType() == IResourceChangeEvent.PRE_CLOSE)
&& event.getResource() instanceof IProject) {
IProject p = (IProject)event.getResource();
clear(p.getFullPath());
return;
}
IResourceDelta delta = event.getDelta();
try {
if(delta != null) {
delta.accept(visitor);
}
} catch (CoreException e) {
ModelPlugin.getDefault().logError(e);
}
}
private synchronized void clear(IFile f) {
uptodate.remove(f);
errorObjects.remove(f);
warningObjects.remove(f);
}
private synchronized void clear(IPath f) {
Iterator<IFile> it = uptodate.iterator();
while(it.hasNext()) {
if(f.isPrefixOf(it.next().getFullPath())) {
it.remove();
}
}
it = errorObjects.keySet().iterator();
while(it.hasNext()) {
if(f.isPrefixOf(it.next().getFullPath())) {
it.remove();
}
}
it = warningObjects.keySet().iterator();
while(it.hasNext()) {
if(f.isPrefixOf(it.next().getFullPath())) {
it.remove();
}
}
}
ResourceDeltaVisitor visitor = new ResourceDeltaVisitor();
class ResourceDeltaVisitor implements IResourceDeltaVisitor {
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
IResource r = delta.getResource();
if(delta.getKind() == IResourceDelta.REMOVED) {
if(r instanceof IFile) {
clear((IFile)r);
} else {
clear(r.getFullPath());
}
return false;
} else if(delta.getKind() == IResourceDelta.ADDED) {
return false;
} else if(delta.getKind() == IResourceDelta.NO_CHANGE) {
return true;
} else {
if(r instanceof IFile) {
IFile f = (IFile)r;
if(uptodate.contains(f)) {
synchronized(this) {
uptodate.remove(f);
}
if(f.exists()) {
updateJob.add(f);
}
}
}
}
return true;
}
}
UpdateJob updateJob = new UpdateJob();
class UpdateJob extends UIJob {// implements XRunnable {
Set<IFile> fs = new HashSet<IFile>();
boolean running = false;
UpdateJob() {
super(Display.getDefault(), "XMarkerManager");
}
public String getId() {
return "XMarkerManager"; //$NON-NLS-1$
}
// public void run() {
public IStatus runInUIThread(IProgressMonitor monitor) {
synchronized (this) {
running = true;
}
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// return Status.OK_STATUS;
// }
try {
IFile f = null;
while((f = nextFile()) != null) {
reload(f);
}
} finally {
synchronized (this) {
running = false;
}
}
return Status.OK_STATUS;
}
synchronized IFile nextFile() {
if(fs.isEmpty()) {
return null;
}
IFile f = fs.iterator().next();
fs.remove(f);
return f;
}
public synchronized void add(IFile f) {
if(fs.contains(f)) {
return;
}
fs.add(f);
if(!running) {
// XJob.addRunnable(updateJob);
running = true;
schedule(100);
}
}
}
void forceReload(IFile file) {
synchronized (this) {
uptodate.remove(file);
}
reload(file);
}
public void reload(IFile file) {
synchronized (this) {
if(uptodate.contains(file)) {
return;
} else {
uptodate.add(file);
}
}
IMarker[] ms = new IMarker[0];
try {
ms = (!file.isAccessible())
? ModelPlugin.getWorkspace().getRoot().findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE)
: file.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
} catch (CoreException e) {
ModelPlugin.getPluginLog().logError(e);
}
Set<XMarker> xms = new HashSet<XMarker>();
synchronized (this) {
if(file != null && file.isAccessible()) {
Set<XMarker> xms1 = getMarkers(file);
if(xms1 != null) xms.addAll(xms1);
}
}
Set<XModelObject> os;
synchronized (this) {
os = errorObjects.get(file);
if(os == null) {
os = new HashSet<XModelObject>();
errorObjects.put(file, os);
}
}
reload(ms, file, xms, os, IMarker.SEVERITY_ERROR);
synchronized (this) {
os = warningObjects.get(file);
if(os == null) {
os = new HashSet<XModelObject>();
warningObjects.put(file, os);
}
}
xms.clear(); //xmarkers are all errors
reload(ms, file, xms, os, IMarker.SEVERITY_WARNING);
}
void reload(IMarker[] ms, IFile file, Set<XMarker> xms, Set<XModelObject> objects, int severity) {
Set<XModelObject> es = new HashSet<XModelObject>();
for (int i = 0; i < ms.length; i++) {
if(severity != ms[i].getAttribute(IMarker.SEVERITY, 0)) continue;
IResource r = ms[i].getResource();
XModelObject o = EclipseResourceUtil.getObjectByResource(r);
if(o == null) o = EclipseResourceUtil.createObjectForResource(r);
if(o == null) continue;
String path = ms[i].getAttribute("path", null); //$NON-NLS-1$
o = (path == null) ? o : o.getModel().getByPath(path);
if(o == null) continue;
es.add(o);
String attr = ms[i].getAttribute("attribute", null); //$NON-NLS-1$
if(attr != null && attr.length() > 0) {
((XModelObjectImpl)o).addErrorAttributeDirty(attr);
}
}
if(!xms.isEmpty()) {
XModelObject o = EclipseResourceUtil.getObjectByResource(file);
if(o == null) o = EclipseResourceUtil.createObjectForResource(file);
if(o != null) {
for (XMarker m: xms) {
String path = m.getPath();
XModelObject c = o.getModel().getByPath(path);
if(c != null) {
es.add(c);
String attr = m.getAttribute();
if(attr != null && attr.length() > 0) {
((XModelObjectImpl)c).addErrorAttributeDirty(attr);
}
}
}
}
}
Set<XModelObject> copy = new HashSet<XModelObject>();
Set<XModelObject> toRemove = new HashSet<XModelObject>();
Set<XModelObject> toAdd = new HashSet<XModelObject>();
synchronized(this) {
copy.addAll(objects);
}
Iterator<XModelObject> it = copy.iterator();
while(it.hasNext()) {
XModelObject o = it.next();
if(!es.contains(o)) {
if(o.getErrorState() == severity) {
o.setErrorState(0);
}
toRemove.add(o);
} else if(es.contains(o)) {
if(severity > o.getErrorState()) {
o.setErrorState(severity);
} else {
((XModelObjectImpl)o).commitErrorAttributes();
}
es.remove(o);
}
}
it = es.iterator();
while(it.hasNext()) {
XModelObject o = (XModelObject)it.next();
if(severity > o.getErrorState()) {
o.setErrorState(severity);
} else {
((XModelObjectImpl)o).commitErrorAttributes();
}
if(!objects.contains(o)) {
toAdd.add(o);
}
}
synchronized(this) {
objects.removeAll(toRemove);
objects.addAll(toAdd);
}
}
void update(XModelObject object) {
XModelObject fo = FileSystemsHelper.getFile(object);
if(fo != null) {
IFile f = (IFile)fo.getAdapter(IFile.class);
if(f != null && f.exists()) {
reload(f);
}
}
}
public int getErrorState(XModelObject object) {
if(object == null) return 0;
update(object);
if(object.getErrorState() == IMarker.SEVERITY_ERROR || object.getErrorChildCount() > 0) return IMarker.SEVERITY_ERROR;
if(object.getErrorState() == IMarker.SEVERITY_WARNING || object.getWarningChildCount() > 0) return IMarker.SEVERITY_WARNING;
return 0;
}
public boolean hasErrors(XModelObject object) {
update(object);
return object != null && (object.getErrorState() == IMarker.SEVERITY_ERROR || object.getErrorChildCount() > 0);
}
public boolean hasWarnings(XModelObject object) {
update(object);
return object != null && (object.getErrorState() == IMarker.SEVERITY_WARNING || object.getWarningChildCount() > 0);
}
public boolean hasErrors(XModelObject object, String attribute) {
update(object);
if(attribute == null) return hasErrors(object);
if(object.getErrorState() == 0) return false;
return object.getAttributeErrorState(attribute);
}
public String getError(XModelObject object, String attribute) {
XModelObject f = ((XModelObjectImpl)object).getResourceAncestor();
IFile file = (f == null) ? null : (IFile)f.getAdapter(IFile.class);
if(file == null) return null;
IMarker[] ms = null;
try {
ms = file.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
} catch (CoreException e) {
//ignore no markers - no problem
return null;
}
if(ms == null) return null;
String pathInFile = object.getPath().substring(f.getPath().length());
for (int i = 0; i < ms.length; i++) {
XModelObject o = object;
String path = ms[i].getAttribute("path", null); //$NON-NLS-1$
if(path != null && !path.endsWith(pathInFile)) {
continue;
}
o = (path == null) ? o : o.getModel().getByPath(path);
if(o == null) continue;
String attr = ms[i].getAttribute("attribute", null); //$NON-NLS-1$
if(attr != null && attr.equals(attribute)) {
return ms[i].getAttribute(IMarker.MESSAGE, null);
}
}
Set<XMarker> xms = getMarkers(file);
if(xms != null) {
for (XMarker m: xms) {
XModelObject o = object;
String path = m.getPath();
if(path != null && !path.endsWith(pathInFile)) {
continue;
}
o = (path == null) ? o : o.getModel().getByPath(path);
if(o == null) continue;
String attr = m.getAttribute();
if(attr != null && attr.equals(attribute)) {
return m.getMessage();
}
}
}
return null;
}
Set<XMarker> getMarkers(IFile file) {
return xmarkers.get(file);
}
synchronized void clearXMarkers(IFile file, Set<XMarker> removed) {
Set<XMarker> all = getMarkers(file);
if(all != null) {
all.removeAll(removed);
}
}
synchronized void addXMarkers(IFile file, Set<XMarker> added) {
Set<XMarker> all = getMarkers(file);
if(all == null) {
all = new HashSet<XMarker>();
xmarkers.put(file, all);
}
all.addAll(added);
}
}
/*local*/ class XMarker {
String type;
String message;
String path;
String attribute;
public XMarker() {}
public void setType(String s) {
type = s;
}
public void setMessage(String s) {
message = s;
}
public void setPath(String s) {
path = s;
}
public void setAttribute(String s) {
attribute = s;
}
public String getType() {
return type;
}
public String getMessage() {
return message;
}
public String getPath() {
return path;
}
public String getAttribute() {
return attribute;
}
}