blob: a14e0dbaaf20c89fe4cc2a31e498bfa2b463f721 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2021 THALES GLOBAL SERVICES.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.tools.api.ui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.sirius.business.api.query.EObjectQuery;
import org.eclipse.sirius.business.api.query.ResourceQuery;
import org.eclipse.sirius.business.api.session.Session;
import org.eclipse.sirius.business.api.session.SessionManager;
import org.eclipse.sirius.ext.base.Option;
import org.eclipse.sirius.ext.base.Options;
import org.eclipse.sirius.viewpoint.DRepresentation;
import org.eclipse.sirius.viewpoint.Messages;
import com.google.common.base.Preconditions;
/**
* A class providing useful methods for refresh.
*
* @author mbats
*/
public final class RefreshHelper {
private static List<Predicate<Notification>> impactingNotificationPredicates = new ArrayList<>();
/**
* Prevent instantiation.
*/
private RefreshHelper() {
}
/**
* Checks whether at least one change of which we are notified, concerns a semantic model or a specific graphical
* change (registered through {@link #registerImpactingNotification(Predicate)}).
*
* @param notifications
* the model changes.
* @return <code>true</code> if the changes impact a semantic model or a specific graphical change.
*/
public static boolean isImpactingNotification(final Collection<Notification> notifications) {
// Create caches for the resource of a notifier and for the result of the method "new
// ResourceQuery(Resource).isAirdOrSrmResource()". Both of them can be "time consuming".
Map<EObject, Boolean> notifierIsInAirdOrSrmResource = new HashMap<>();
Map<EObject, Resource> notifierWithResource = new HashMap<>();
Set<EObject> alreadyDoneNotifiers = new HashSet<>();
for (Notification notification : notifications) {
Object notifier = notification.getNotifier();
if (notifier instanceof EObject) {
EObject eObjectNotifier = (EObject) notifier;
if (!alreadyDoneNotifiers.contains(eObjectNotifier)
&& isImpactingNotification(notification, eObjectNotifier, alreadyDoneNotifiers, notifierWithResource, notifierIsInAirdOrSrmResource)) {
return true;
}
}
}
return false;
}
/**
* Checks whether this notification concerns a semantic model change or a specific graphical change (registered
* through {@link #registerImpactingNotification(Predicate)}).
*
* @param notification
* the model change.
* @param notifier
* the EObject which is concerned by this notification
* @param alreadyDoneNotifiers
* list of notifiers that have already been checked before
* @param notifierWithResource
* a cache map used to retrieve the resource from a notifier
* @param notifierIsInAirdOrSrmResource
* a cache map used to retrieve the result of the method
* <code>ResourceQuery(Resource).isAirdOrSrmResource()</code> from a notifier
* @return <code>true</code> if the change impact a semantic model or a specific graphical change.
*/
protected static boolean isImpactingNotification(Notification notification, EObject notifier, Set<EObject> alreadyDoneNotifiers, Map<EObject, Resource> notifierWithResource,
Map<EObject, Boolean> notifierIsInAirdOrSrmResource) {
Resource notifierResource = notifierWithResource.get(notifier);
Boolean isAirdOrSrmResourceCache = notifierIsInAirdOrSrmResource.get(notifier);
boolean isImpactingNotification = false;
if (notifierResource != null) {
isImpactingNotification = !isAirdOrSrmResourceCache.booleanValue();
} else {
notifierResource = notifier.eResource();
if (notifierResource != null) {
if (impactingNotificationPredicates.isEmpty()) {
// If the impactingNotificationPredicates is not empty we must check every notification but in other
// case, you have only one check by notifier.
alreadyDoneNotifiers.add(notifier);
}
isImpactingNotification = !new ResourceQuery(notifierResource).isAirdOrSrmResource();
notifierWithResource.put(notifier, notifierResource);
notifierIsInAirdOrSrmResource.put(notifier, !isImpactingNotification);
}
}
if (notifierResource != null && !isImpactingNotification && isSpecificImpactingNotification(notification)) {
// The impacting notification is a graphical one, so if we are in manual refresh, we must add
// the corresponding representation to the force refresh list
Option<DRepresentation> optionalDRepresentation = Options.newNone();
Session session = SessionManager.INSTANCE.getExistingSession(notifierResource.getURI());
if (session == null) {
// Maybe the notifier resource is not the session resource (for example a SRM resource in
// Collab), in this case, use a more global session search.
optionalDRepresentation = new EObjectQuery(notifier).getRepresentation();
if (optionalDRepresentation.some()) {
session = new EObjectQuery(optionalDRepresentation.get()).getSession();
}
}
if (session != null && !session.getSiriusPreferences().isAutoRefresh()) {
if (!optionalDRepresentation.some()) {
optionalDRepresentation = new EObjectQuery(notifier).getRepresentation();
}
if (optionalDRepresentation.some()) {
session.getRefreshEditorsListener().addRepresentationToForceRefresh(optionalDRepresentation.get());
}
}
isImpactingNotification = true;
}
return isImpactingNotification;
}
/**
* Check this notification with all registered predicates.
*
* @param notification
* The notification to check
* @return true if at least one predicate consider this notification as impacting, false otherwise.
*/
private static boolean isSpecificImpactingNotification(Notification notification) {
// Check graphical modification considered as impacting by customers
for (Predicate<Notification> predicate : impactingNotificationPredicates) {
if (predicate.test(notification)) {
return true;
}
}
return false;
}
/**
* Register a predicate to consider a graphical notification (notification concerning the representations file and
* not the semantic file) as impacting and requiring a refresh of the diagram.
*
* @param impactingNotificationPredicate
* a predicate to consider a notification as impacting.
*/
public static void registerImpactingNotification(Predicate<Notification> impactingNotificationPredicate) {
Preconditions.checkNotNull(impactingNotificationPredicate, Messages.RefreshHelper_notNullPredicate);
impactingNotificationPredicates.add(impactingNotificationPredicate);
}
/**
* Unregister a predicate, previously register through {@link #registerImpactingNotification(Predicate)}.
*
* @param impactingNotificationPredicate
* a predicate that must no longer consider a notification as impacting.
*/
public static void unregisterImpactingNotification(Predicate<Notification> impactingNotificationPredicate) {
Preconditions.checkNotNull(impactingNotificationPredicate, Messages.RefreshHelper_notNullPredicate);
impactingNotificationPredicates.remove(impactingNotificationPredicate);
}
}