org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder.java

Source

/*******************************************************************************
 * Copyright (c) 2010 THALES GLOBAL SERVICES.
 * 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:
 *    Obeo - initial API and implementation
 *******************************************************************************/
package org.eclipse.sirius.diagram.sequence.business.internal.util;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent;
import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor;
import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent;
import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline;
import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message;
import org.eclipse.sirius.diagram.sequence.util.Range;
import org.eclipse.sirius.ext.base.Option;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * Helper to locate sequence events inside a given context event.
 * 
 * @author pcdavid
 */
public class EventFinder {
    private final ISequenceEvent context;

    private final Lifeline lifeline;

    private Range expansionZone;

    private Predicate<ISequenceEvent> eventsToIgnore;

    private boolean reconnect;

    private boolean reparent;

    private Function<ISequenceEvent, Range> verticalRangeFunction = new Function<ISequenceEvent, Range>() {
        public Range apply(ISequenceEvent from) {
            return from.getVerticalRange();
        }
    };

    private Map<AbstractNodeEvent, ISequenceEvent> reparented = Maps.newHashMap();

    /**
     * Constructor.
     * 
     * @param context
     *            the event in which to look for. Only sub-events of the context
     *            are considered in the search.
     */
    public EventFinder(Lifeline context) {
        this.context = context;
        this.lifeline = context;
    }

    /**
     * Constructor.
     * 
     * @param context
     *            the event in which to look for. Only sub-events of the context
     *            are considered in the search.
     */
    public EventFinder(AbstractNodeEvent context) {
        this.context = context;
        Preconditions.checkArgument(context.getLifeline().some());
        this.lifeline = context.getLifeline().get();
    }

    /**
     * Constructor.
     * 
     * @param context
     *            the event in which to look for. Only sub-events of the context
     *            are considered in the search.
     * 
     * @param lifeline
     *            the lifeline on which to look for an event.
     */
    public EventFinder(ISequenceEvent context, Lifeline lifeline) {
        this.context = context;
        this.lifeline = lifeline;
    }

    /**
     * Allow to use another way to compute the range of the context (iG range
     * after move?..).
     * 
     * @param rangeFunction
     *            the range function to use to compute the context range.
     */
    public void setVerticalRangefunction(Function<ISequenceEvent, Range> rangeFunction) {
        if (verticalRangeFunction != null) {
            this.verticalRangeFunction = rangeFunction;
        }
    }

    public void setReconnection(boolean mode) {
        this.reconnect = mode;
    }

    public boolean isReconnection() {
        return reconnect;
    }

    public void setReparent(boolean mode) {
        this.reparent = mode;
    }

    public boolean isReparent() {
        return reparent;
    }

    public void setExpansionZone(Range expansionZone) {
        this.expansionZone = expansionZone;
    }

    public void setEventsToIgnore(Predicate<ISequenceEvent> eventsToIgnore) {
        this.eventsToIgnore = eventsToIgnore;
    }

    /**
     * Returns the deepest event in the hierarchy of sub-event starting from
     * this finder's context which includes completely the specified range.
     * 
     * @param range
     *            the range to look for.
     * @return the deepest event, starting from this finder's context, which
     *         includes the specified range, or <code>null</code>.
     */
    public ISequenceEvent findMostSpecificEvent(Range range) {
        if (context instanceof Message) {
            return null;
        }
        ISequenceEvent result = null;
        if (contextIncludesRange(range)) {
            if (context != null && !shouldIgnore().apply(context)) {
                boolean okForReconnection = !isReconnection() || !Message.NO_RECONNECTABLE_EVENTS.apply(context);
                boolean okForReparent = !isReparent() || !AbstractNodeEvent.NO_REPARENTABLE_EVENTS.apply(context);
                if (okForReconnection && okForReparent) {
                    result = context;
                }
            }
        }
        if (contextIncludesRange(range) || isReparent()) {
            Predicate<ISequenceEvent> sameLifeline = new SameLifelinePredicate(lifeline);
            List<ISequenceEvent> eventsToInspect = Lists.newArrayList();
            if ((reconnect || reparent) && (context instanceof AbstractNodeEvent || context instanceof Lifeline)) {
                for (View view : Iterables.filter(context.getNotationView().getChildren(), View.class)) {
                    Option<ISequenceEvent> ise = ISequenceElementAccessor.getISequenceEvent(view);
                    if (ise != null && ise.some() && !reparented.containsKey(ise.get())) {
                        eventsToInspect.add(ise.get());
                    }
                }

                // handle reparented events
                // look for new child
                for (Entry<AbstractNodeEvent, ISequenceEvent> entry : reparented.entrySet()) {
                    if (entry.getValue() == context) {
                        eventsToInspect.add(entry.getKey());
                    }
                }
            } else {
                eventsToInspect.addAll(context.getSubEvents());
            }
            for (ISequenceEvent child : Iterables.filter(eventsToInspect,
                    Predicates.and(sameLifeline, Predicates.not(shouldIgnore())))) {
                EventFinder childFinder = new EventFinder(child, lifeline);
                childFinder.setReconnection(isReconnection());
                childFinder.setReparent(isReparent());
                childFinder.setEventsToIgnore(eventsToIgnore);
                childFinder.setExpansionZone(expansionZone);
                childFinder.setVerticalRangefunction(verticalRangeFunction);
                childFinder.setReparented(reparented);
                ISequenceEvent moreSpecific = childFinder.findMostSpecificEvent(range);
                if (moreSpecific != null) {
                    result = moreSpecific;
                    break;
                }
            }
        }

        return result;
    }

    /**
     * Tests whether this finder's context element includes the specified range,
     * considering the optional expansion step.
     */
    private boolean contextIncludesRange(Range range) {
        Range currentContextRange = verticalRangeFunction.apply(context);
        return getRangeAfterExpansion(currentContextRange).includes(range);
    }

    /**
     * Returns a predicate which tests whether an element should be ignored in
     * the search for descendants.
     */
    private Predicate<ISequenceEvent> shouldIgnore() {
        if (eventsToIgnore == null) {
            return Predicates.alwaysFalse();
        } else {
            return eventsToIgnore;
        }
    }

    private Range getRangeAfterExpansion(Range range) {
        if (expansionZone != null && range.includesAtLeastOneBound(expansionZone)) {
            return range.union(expansionZone);
        } else {
            return range;
        }
    }

    /**
     * Handle already reparented event.
     * 
     * @param newParents
     *            key is the reparented exec, value is the new parent.
     */
    public void setReparented(Map<AbstractNodeEvent, ISequenceEvent> newParents) {
        this.reparented.putAll(newParents);
    }
}