/**
* This file Copyright (c) 2005-2008 Aptana, Inc. This program is
* dual-licensed under both the Aptana Public License and the GNU General
* Public license. You may elect to use one or the other of these licenses.
*
* This program is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. Redistribution, except as permitted by whichever of
* the GPL or APL you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or modify this
* program under the terms of the GNU General Public License,
* Version 3, as published by the Free Software Foundation. You should
* have received a copy of the GNU General Public License, Version 3 along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Aptana provides a special exception to allow redistribution of this file
* with certain other free and open source software ("FOSS") code and certain additional terms
* pursuant to Section 7 of the GPL. You may view the exception and these
* terms on the web at http://www.aptana.com/legal/gpl/.
*
* 2. For the Aptana Public License (APL), this program and the
* accompanying materials are made available under the terms of the APL
* v1.0 which accompanies this distribution, and is available at
* http://www.aptana.com/legal/apl/.
*
* You may view the GPL, Aptana's exception and additional terms, and the
* APL in the file titled license.html at the root of the corresponding
* plugin containing this source file.
*
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.ide.internal.core.resources;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.internal.resources.IMarkerSetElement;
import org.eclipse.core.internal.resources.MarkerTypeDefinitionCache;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Status;
import com.aptana.ide.core.AptanaCorePlugin;
import com.aptana.ide.core.IdeLog;
import com.aptana.ide.core.StringUtils;
import com.aptana.ide.core.resources.IUniformResource;
import com.aptana.ide.core.resources.IUniformResourceChangeListener;
import com.aptana.ide.internal.core.events.UniformResourceChangeEvent;
/**
* @author Max Stepanov
*
*/
public final class MarkerManager {
private static final MarkerInfo[] NO_MARKER_INFO = new MarkerInfo[0];
private static MarkerManager instance;
private MarkerTypeDefinitionCache cache = new MarkerTypeDefinitionCache();
private long nextMarkerId = 0;
private Map resources = new HashMap();
private ListenerList listeners = new ListenerList();
private IMarker rootMarker;
private Map currentDeltas = null;
private Object lock = new Object();
/**
* getInstance
*
* @return MarkerManager
*/
public static MarkerManager getInstance() {
if ( instance == null ) {
instance = new MarkerManager();
}
return instance;
}
private MarkerManager() {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
try {
rootMarker = workspace.getRoot().createMarker("com.aptana.ide.internal.core.resources.ExternalResourcesMarker"); //$NON-NLS-1$
} catch (CoreException e) {
IdeLog.logError(AptanaCorePlugin.getDefault(),StringUtils.EMPTY,e);
}
if ( rootMarker != null ) {
workspace.addResourceChangeListener( new IResourceChangeListener() {
public void resourceChanged(IResourceChangeEvent event) {
handleResourceChanged();
}
}, IResourceChangeEvent.PRE_BUILD);
}
}
/**
* findMarkerInfo
*
* @param resource
* @param id
* @return MarkerInfo
*/
public MarkerInfo findMarkerInfo(IUniformResource resource, long id) {
ResourceInfo info = getResourceInfo(resource);
if ( info == null ) {
return null;
}
MarkerSet markers = info.getMarkers(false);
if ( markers == null ) {
return null;
}
return (MarkerInfo) markers.get(id);
}
/**
* add
*
* @param resource
* @param marker
* @throws CoreException
*/
public void add(IUniformResource resource, MarkerInfo marker) throws CoreException {
ResourceInfo info = getResourceInfo(resource);
if ( info == null ) {
info = createResourceInfo(resource);
}
MarkerSet markers = info.getMarkers(true);
if ( markers == null ) {
markers = new MarkerSet(1);
}
basicAdd(resource,markers,marker);
if ( !markers.isEmpty() ) {
info.setMarkers(markers);
}
IMarkerSetElement[] changes = new IMarkerSetElement[1];
changes[0] = new MarkerDelta(IResourceDelta.ADDED,resource,marker);
changedMarkers(resource,changes);
}
/**
* isPersistent
*
* @param info
* @return boolean
*/
public boolean isPersistent(MarkerInfo info) {
if ( !cache.isPersistent(info.getType()) ) {
return false;
}
Object isTransient = info.getAttribute(IMarker.TRANSIENT);
return isTransient == null || !(isTransient instanceof Boolean) || !((Boolean) isTransient).booleanValue();
}
/**
* removeMarker
*
* @param resource
* @param id
* @throws CoreException
*/
public void removeMarker(IUniformResource resource, long id) throws CoreException {
MarkerInfo marker = findMarkerInfo(resource,id);
if ( marker == null ) {
return;
}
ResourceInfo info = getResourceInfo(resource);
MarkerSet markers = info.getMarkers(true);
int size = markers.size();
markers.remove(marker);
info.setMarkers(markers.size() == 0 ? null : markers);
if ( markers.size() != size ) {
/* TODO: store persistent marker state */
IMarkerSetElement[] changes = new IMarkerSetElement[] { new MarkerDelta(IResourceDelta.REMOVED,resource,marker) };
changedMarkers(resource,changes);
}
}
/**
* changedMarkers
*
* @param resource
* @param changes
* @throws CoreException
*/
public void changedMarkers(IUniformResource resource, IMarkerSetElement[] changes) throws CoreException {
if (changes == null || changes.length == 0) {
return;
}
URI uri = resource.getURI();
synchronized (lock) {
if ( currentDeltas == null ) {
currentDeltas = new HashMap();
}
MarkerSet previousChanges = (MarkerSet) currentDeltas.get(uri);
MarkerSet result = MarkerDelta.merge(previousChanges, changes);
if (result.size() == 0) {
currentDeltas.remove(uri);
} else {
currentDeltas.put(uri, result);
}
}
if ( rootMarker != null ) {
rootMarker.setAttribute("updateId",rootMarker.getAttribute("updateId",0)+1); //$NON-NLS-1$ //$NON-NLS-2$
}
}
/**
* isSubtype
*
* @param type
* @param superType
* @return boolean
*/
public boolean isSubtype(String type, String superType) {
return cache.isSubtype(type,superType);
}
/**
* findMarkersInfo
*
* @param resource
* @param type
* @param includeSubtypes
* @return MarkerInfo[]
*/
public MarkerInfo[] findMarkersInfo(IUniformResource resource, String type, boolean includeSubtypes) {
ArrayList result = new ArrayList();
ResourceInfo info = getResourceInfo(resource);
if ( info == null ) {
return NO_MARKER_INFO;
}
MarkerSet markers = info.getMarkers(false);
if ( markers == null ) {
return NO_MARKER_INFO;
}
IMarkerSetElement[] elements = markers.elements();
for( int i = 0; i < elements.length; ++i ) {
MarkerInfo marker = (MarkerInfo) elements[i];
if ( type == null ) {
result.add(marker);
} else {
if (includeSubtypes) {
if (isSubtype(marker.getType(), type)) {
result.add(marker);
}
} else {
if (marker.getType().equals(type)) {
result.add(marker);
}
}
}
}
if (result.size() == 0) {
return NO_MARKER_INFO;
}
return (MarkerInfo[]) result.toArray(new MarkerInfo[result.size()]);
}
private ResourceInfo getResourceInfo(IUniformResource resource) {
return (ResourceInfo) resources.get(resource.getURI());
}
private ResourceInfo createResourceInfo(IUniformResource resource) {
ResourceInfo info = new ResourceInfo();
resources.put(resource.getURI(),info);
return info;
}
private void basicAdd( IUniformResource resource, MarkerSet markers, MarkerInfo newMarker) throws CoreException {
if (newMarker.getId() != MarkerInfo.UNDEFINED_ID) {
throw new CoreException( new Status(IStatus.ERROR, AptanaCorePlugin.ID, IStatus.OK, Messages.MarkerManager_MarkerIDIsDefined, null));
}
newMarker.setId(nextMarkerId());
markers.add(newMarker);
/* TODO: store persistent marker state*/
}
private long nextMarkerId() {
return nextMarkerId++;
}
/**
* addResourceChangeListener
*
* @param listener
*/
public void addResourceChangeListener( IUniformResourceChangeListener listener ) {
listeners.add(listener);
}
/**
* removeResourceChangeListener
*
* @param listener
*/
public void removeResourceChangeListener( IUniformResourceChangeListener listener ) {
listeners.remove(listener);
}
/**
* Notifies manager that external resource is changed.
* @param resource - resource that is changed.
*/
public void externalResourceChanged(IUniformResource resource)
{
handleResourceChanged();
}
private void handleResourceChanged() {
if ( currentDeltas == null ) {
return;
}
MarkerSet[] markers;
synchronized (lock) {
markers = (MarkerSet[]) currentDeltas.values().toArray(new MarkerSet[currentDeltas.size()]);
currentDeltas = null;
}
Object[] list = listeners.getListeners();
for( int j = 0; j < markers.length; ++j ) {
IMarkerDelta[] deltas = new IMarkerDelta[markers[j].size()];
markers[j].copyInto(deltas);
IUniformResource resource = null;
if ( deltas.length > 0 && deltas[0] instanceof MarkerDelta ) {
resource = ((MarkerDelta)deltas[0]).getUniformResource();
}
UniformResourceChangeEvent event = new UniformResourceChangeEvent(this,resource,deltas);
for( int i = 0; i < list.length; ++i ) {
try {
((IUniformResourceChangeListener)list[i]).resourceChanged(event);
} catch( Exception e ) {
IdeLog.logError(AptanaCorePlugin.getDefault(),StringUtils.EMPTY,e);
}
}
}
}
}