/******************************************************************************* * Copyright (c) 2016 itemis AG 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: * Alexander Nyßen (itemis AG) - initial API and implementation * Matthias Wienand (itemis AG) - initial API and implementation * *******************************************************************************/ package org.eclipse.gef.fx.anchors; import java.util.Collection; import java.util.HashSet; import java.util.Set; import org.eclipse.gef.geometry.planar.Point; import javafx.beans.binding.Binding; import javafx.beans.property.ObjectPropertyBase; import javafx.beans.value.ObservableValue; import javafx.scene.Node; /** * The {@link IComputationStrategy} is responsible for computing anchor * positions based on the anchorage {@link Node}, the anchored {@link Node}, and * respective (strategy-specific) {@link Parameter parameters}. * * @author anyssen * @author mwienand */ public interface IComputationStrategy { /** * Base class for all computation parameters that can be passed to an * {@link IComputationStrategy}. * * @param <T> * The parameter value type. */ public abstract class Parameter<T> extends ObjectPropertyBase<T> { /** * Indicates whether the parameter value can be shared to compute * positions of all attached anchors or not. */ public enum Kind { /** * Indicates that the parameter value may be shared to compute the * position for all attached {@link AnchorKey}, as the value depends * on the anchorage {@link Node} (to which the anchor is bound) and * not on an individual attached anchored {@link Node}. */ ANCHORAGE, /** * Indicates that the parameter value may not be shared, i.e. an * individual value is required to compute the position for each * attached {@link AnchorKey}, e.g. because the value depends on the * anchored node. */ ANCHORED }; /** * Retrieves a parameter of the respective type from the set of given * parameters. * * @param <T> * The runtime type of the parameter. * @param parameters * The set of parameters to search. * @param parameterType * The parameter type * @return The parameter or <code>null</code>. */ @SuppressWarnings("unchecked") protected static <T extends Parameter<?>> T get( Collection<? extends Parameter<?>> parameters, Class<T> parameterType) { Set<T> parametersOfType = new HashSet<>(); for (Parameter<?> p : parameters) { if (parameterType.equals(p.getClass())) { parametersOfType.add((T) p); } } if (parametersOfType.isEmpty()) { return null; } else if (parametersOfType.size() > 1) { // this should already be guarded, but we provide an additional // check here throw new IllegalArgumentException( "The given set of parameters contains " + parameters.size() + " parameters of type " + parameterType.getSimpleName() + ": " + parameters); } else { // TODO: create one if needed (using default constructor) return parametersOfType.iterator().next(); } } /** * Returns the {@link Kind} returned by an instance of the given * {@link Parameter} type. * * @param paramType * The {@link Parameter} type for which to return the * {@link Kind}. * @return The {@link Kind} for the given {@link Parameter} type. */ public static Kind getKind(Class<? extends Parameter<?>> paramType) { try { return paramType.newInstance().getKind(); } catch (Exception e) { throw new IllegalArgumentException( "Cannot instantiate parameter of type " + paramType.getSimpleName() + ".", e); } } /** * Returns <code>true</code> if an instance of the given * {@link Parameter} type is optional. Otherwise returns * <code>false</code>. * * @param paramType * The {@link Parameter} type for which to determine * optionality. * @return <code>true</code> if an instance of the given * {@link Parameter} type is optional, otherwise * <code>false</code>. */ public static boolean isOptional( Class<? extends Parameter<?>> paramType) { try { return paramType.newInstance().isOptional(); } catch (Exception e) { throw new IllegalArgumentException( "Cannot instantiate parameter of type " + paramType.getSimpleName() + ".", e); } } private Kind kind; private boolean optional; private ObservableValue<? extends T> bindingTarget; /** * Creates a new mandatory {@link Parameter} of the given kind. * * @param kind * The parameter kind. */ public Parameter(Kind kind) { this(kind, false); } /** * Creates a new optional parameter of the given kind. * * @param kind * The parameter kin. * * @param optional * Whether this parameter is optional or not. */ public Parameter(Kind kind, boolean optional) { this.kind = kind; this.optional = optional; } @Override public void bind(ObservableValue<? extends T> newObservable) { super.bind(newObservable); this.bindingTarget = newObservable; } @Override public Object getBean() { // no bean by default return null; } /** * Retrieves the {@link Kind} of this parameter, which indicates whether * a single value may be shared to compute the positions of all attached * {@link AnchorKey}s or not. * * @return The parameter {@link Kind}. */ public final Kind getKind() { return kind; } @Override public String getName() { // use type name as property name return getClass().getSimpleName(); } /** * If this parameter is bound, can be used to invalidate the underlying * binding, so that the value is re-computed. */ public void invalidateBinding() { if (isBound() && bindingTarget instanceof Binding) { ((Binding<? extends T>) bindingTarget).invalidate(); } } /** * Indicates whether this parameter is optional * * @return <code>true</code> if the parameter is optional, * <code>false</code> otherwise. */ public final boolean isOptional() { return optional; } @Override public void unbind() { this.bindingTarget = null; super.unbind(); } } /** * Computes an anchor position based on the given anchorage visual, anchored * visual, and anchored reference point. * * @param anchorage * The anchorage visual. * @param anchored * The anchored visual. * @param parameters * The available computation parameters. strategy. * @return The anchor position. */ public Point computePositionInScene(Node anchorage, Node anchored, Set<Parameter<?>> parameters); /** * Returns the types of parameters required by this strategy. * * @return The parameters required by this strategy. */ public Set<Class<? extends Parameter<?>>> getRequiredParameters(); }