blob: 5c78bfca5d3967dd72754f7ebc4e69fea14daed3 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are 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:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.emf.core.edit;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.content.IContentTypeManager;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.gmf.runtime.emf.core.EventTypes;
import org.eclipse.gmf.runtime.emf.core.internal.util.MSLUtil;
/**
* <p>
* MSL filters are used to control what events get sent to listeners. To define
* a new filter, one would subclass <code>MFilter</code> and implement the
* abstract method <code>matches</code>. An MSL listener must have a filter
* to start receiving events. An <code>MListener</code> with a
* <code>null</code> filter will not receive events.
* </p>
* <p>
* The class also defines some pre-defined filters that can be composed to build
* more complex filters. Some of these are filter instances, some are
* classes.
* </p>
*
* @author rafikj
*/
public abstract class MFilter {
/** A wildcard filter, matching all notifications. */
public final static MFilter WILDCARD_FILTER = new MFilter() {
public boolean matches(Notification event) {
return true;
}};
/**
* Filter for resource loaded events.
*
* @see #RESOURCE_UNLOADED_FILTER
*/
public final static MFilter RESOURCE_LOADED_FILTER = new MFilter.And(
new MFilter.And(new MFilter.EventType(Notification.SET),
new MFilter.NotifierType(Resource.class, false)), new MFilter() {
public boolean matches(Notification event) {
if (event.getFeatureID(Resource.class) == Resource.RESOURCE__IS_LOADED) {
return event.getNewBooleanValue();
}
return false;
}
});
/**
* Filter for resource unloaded events.
* @see #RESOURCE_LOADED_FILTER
*
*/
public final static MFilter RESOURCE_UNLOADED_FILTER = new MFilter.And(
new MFilter.And(new MFilter.EventType(Notification.SET),
new MFilter.NotifierType(Resource.class, false)), new MFilter() {
public boolean matches(Notification event) {
if (event.getFeatureID(Resource.class) == Resource.RESOURCE__IS_LOADED) {
return !event.getNewBooleanValue();
}
return false;
}
});
/**
* Filter for root added events.
*
* @see #RESOURCE_ROOT_REMOVED_FILTER
*/
public final static MFilter RESOURCE_ROOT_ADDED_FILTER = new MFilter.And(
new MFilter.NotifierType(Resource.class, false), new MFilter.Or(
new MFilter.EventType(Notification.ADD), new MFilter.EventType(
Notification.ADD_MANY)));
/**
* Filter for root removed events.
*
* @see #RESOURCE_ROOT_ADDED_FILTER
*/
public final static MFilter RESOURCE_ROOT_REMOVED_FILTER = new MFilter.And(
new MFilter.NotifierType(Resource.class, false), new MFilter.Or(
new MFilter.EventType(Notification.REMOVE), new MFilter.EventType(
Notification.REMOVE_MANY)));
/**
* Filter for resource saved events. This filter is the same as the dirtied
* filter, so don't add both.
*
* @see #RESOURCE_DIRTIED_FILTER
*/
public final static MFilter RESOURCE_SAVED_FILTER = new MFilter.And(
new MFilter.EventType(Notification.SET), new MFilter.NotifierType(
Resource.class, false));
/**
* Filter for resource dirtied events. This filter is the same as the saved
* filter, so don't add both.
*
* @see #RESOURCE_SAVED_FILTER
*/
public final static MFilter RESOURCE_DIRTIED_FILTER = new MFilter.And(
new MFilter.EventType(Notification.SET), new MFilter.NotifierType(
Resource.class, false));
/**
* Filter for element separated or absorbed events.
*/
public final static MFilter SEPARATED_ABSORBED_FILTER = new MFilter.Or(
new MFilter.EventType(EventTypes.SEPARATE), new MFilter.EventType(
EventTypes.ABSORB));
/**
* Filter for element created events.
*
* @see #ELEMENT_DELETED_FILTER
* @see #ELEMENT_MODIFIED_FILTER
*/
public final static MFilter ELEMENT_CREATED_FILTER = new MFilter.And(
new MFilter.NotifierType(EObject.class, false), new MFilter.Or(
new MFilter.EventType(Notification.ADD), new MFilter.EventType(
Notification.ADD_MANY)));
/**
* Filter for element deleted events.
*
* @see #ELEMENT_CREATED_FILTER
* @see #ELEMENT_MODIFIED_FILTER
*/
public final static MFilter ELEMENT_DELETED_FILTER = new MFilter.And(
new MFilter.NotifierType(EObject.class, false), new MFilter.Or(
new MFilter.EventType(Notification.REMOVE), new MFilter.EventType(
Notification.REMOVE_MANY)));
/**
* Filter for element modified events.
*
* @see #ELEMENT_CREATED_FILTER
* @see #ELEMENT_DELETED_FILTER
*/
public final static MFilter ELEMENT_MODIFIED_FILTER = new MFilter.And(
new MFilter.EventType(Notification.SET), new MFilter.NotifierType(
EObject.class, false));
/**
* Filter for element loaded events.
*/
public final static MFilter ELEMENT_LOADED_FILTER =
new MFilter.EventType(EventTypes.LOAD);
/**
* Filter for undo interval closed events.
*/
public final static MFilter UNDO_INTERVAL_CLOSED_FILTER = new MFilter.And(
new MFilter.EventType(EventTypes.CREATE), new MFilter.NotifierType(
MUndoInterval.class, false));
/**
* Checks if the event matches the filter.
*
* @param event
* The event to match.
* @return True if event matches the filter, false otherwise.
*/
public abstract boolean matches(Notification event);
/**
* Negate a filter.
*/
public final static class Not
extends MFilter {
private MFilter filter = null;
public Not(MFilter filter) {
super();
this.filter = filter;
}
public boolean matches(Notification event) {
return !filter.matches(event);
}
}
/**
* And two filters.
*/
public final static class And
extends MFilter {
private MFilter filter1 = null;
private MFilter filter2 = null;
public And(MFilter filter1, MFilter filter2) {
super();
this.filter1 = filter1;
this.filter2 = filter2;
}
public boolean matches(Notification event) {
return filter1.matches(event) && filter2.matches(event);
}
}
/**
* Or two filters.
*/
public final static class Or
extends MFilter {
private MFilter filter1 = null;
private MFilter filter2 = null;
public Or(MFilter filter1, MFilter filter2) {
super();
this.filter1 = filter1;
this.filter2 = filter2;
}
public boolean matches(Notification event) {
return filter1.matches(event) || filter2.matches(event);
}
}
/**
* Filter on event type.
*/
public final static class EventType
extends MFilter {
private int eventType = -1;
public EventType(int eventType) {
super();
this.eventType = eventType;
}
public boolean matches(Notification event) {
return event.getEventType() == eventType;
}
}
/**
* Filter on notifier.
*/
public final static class Notifier
extends MFilter {
private WeakReference reference = null;
public Notifier(Object notifier) {
super();
reference = new WeakReference(notifier);
}
public boolean matches(Notification event) {
Object notifier = (reference == null) ? null
: reference.get();
return (event.getNotifier() == notifier);
}
}
/**
* Filter on a collection of notifiers.
*/
public final static class Notifiers
extends MFilter {
private Map references = new WeakHashMap();
public Notifiers() {
super();
}
public void add(Object notifier) {
references.put(notifier, null);
}
public void remove(Object notifier) {
references.remove(notifier);
}
public boolean matches(Notification event) {
return references.containsKey(event.getNotifier());
}
}
/**
* Filter on notifier type.
*/
public final static class NotifierType
extends MFilter {
private Class type = null;
private boolean strict = false;
public NotifierType(Class type, boolean strict) {
super();
this.type = type;
}
public boolean matches(Notification event) {
Object notifier = event.getNotifier();
Class notifierType = notifier.getClass();
if (type == notifierType)
return true;
else if (strict)
return false;
else
return type.isInstance(notifier);
}
}
/**
* Filter on notifier <code>EClass</code>.
*/
public final static class NotifierEClass
extends MFilter {
private EClass eClass = null;
private boolean strict = false;
public NotifierEClass(EClass eClass, boolean strict) {
super();
this.eClass = eClass;
}
public boolean matches(Notification event) {
Object notifier = event.getNotifier();
if (notifier instanceof EObject) {
EClass notifierEClass = ((EObject) notifier).eClass();
if (eClass == notifierEClass)
return true;
else if (strict)
return false;
else
return notifierEClass.getEAllSuperTypes().contains(eClass);
} else
return false;
}
}
/**
* Filter resource events that do not pertain to a given collection of content
* type identifiers (Strings). Filters any non-resource level events.
*/
public final static class ResourceContentType
extends MFilter {
private Collection contentTypes;
/**
* Constructs me.
*
* @param contentTypes A collection of string objects for the
* unique identifiers of distinct content types.
*/
public ResourceContentType(Collection contentTypes) {
super();
assert contentTypes != null;
this.contentTypes = contentTypes;
}
public boolean matches(Notification event) {
Object notifier = event.getNotifier();
if (notifier instanceof Resource) {
return resourceMatchesContentTypes((Resource)notifier);
}
return false;
}
/**
* Determines if a provided resource matches one of the provided
* content types.
*
* @param r A valid MSL resource that belongs to an MSL editing domain.
*
* @return true if the resource's content type matches, false otherwise
*/
private boolean resourceMatchesContentTypes(Resource r) {
IContentTypeManager manager = Platform.getContentTypeManager();
IContentType[] types = manager.findContentTypesFor(MSLUtil.getFilePath(r));
for (int i=0; i<types.length; i++) {
if (contentTypes.contains(types[i].getId())) {
return true;
}
}
return false;
}
}
}