/******************************************************************************* * Copyright (c) 2014, 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 * *******************************************************************************/ package org.eclipse.gef.common.adapt; import org.eclipse.gef.common.adapt.inject.AdapterMaps; import com.google.common.reflect.TypeToken; /** * A pair of {@link Class} key and {@link String} role to register adapters at * and retrieve them from {@link IAdaptable}s. Using an {@link AdapterKey} * instead of just a {@link Class} or {@link TypeToken} key allows to register * several adapters under the same key, serving different roles. Nevertheless, * adapters can still be accessed in a type-safe manner. To register a default * adapter for a certain {@link Class} or {@link TypeToken} key, the * {@link #DEFAULT_ROLE} may be used. * <P> * Creating {@link AdapterKey}s is supported by {@link #get(Class, String)} and * {@link #get(TypeToken, String)}, as well as {@link #get(Class)} and * {@link #get(TypeToken)} respectively, where the latter two will use the * {@link #DEFAULT_ROLE}. * * @author anyssen * * @param <T> * The type parameter corresponding to the type parameter of the * {@link Class} used as key ({@link #getKey()}). */ public class AdapterKey<T> implements Comparable<AdapterKey<T>> { /** * A default role to be used for {@link AdapterKey}s. * * @see #get(Class) */ public static final String DEFAULT_ROLE = "default"; /** * Returns an {@link AdapterKey} with no type key and the 'default' role, * which can only be used in adapter map bindings. See {@link AdapterMaps}. * * @return An AdapterKey without type key, using the 'default' role. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static AdapterKey<?> defaultRole() { return get((Class) null, DEFAULT_ROLE); } /** * Creates a new {@link AdapterKey} for the given raw type key and the * {@link #DEFAULT_ROLE} role, which can be used to retrieve an adapter from * an IAdaptable. * * @param <T> * The adapter type. * @param key * The key to use for the newly created {@link AdapterKey}. May * not be <code>null</code>. * @return A new {@link AdapterKey} for the given key and role. * * @see #get(Class, String) */ public static <T> AdapterKey<T> get(Class<T> key) { return get(TypeToken.of(key), DEFAULT_ROLE); } /** * Creates a new {@link AdapterKey} for the given key and role. * * @param <T> * The adapter type. * @param key * The key to use for the newly created {@link AdapterKey}. May * not be <code>null</code>. * @param role * The role to use for the newly created {@link AdapterKey}. May * not be <code>null</code>. * @return A new {@link AdapterKey} for the given key and role. */ public static <T> AdapterKey<T> get(Class<T> key, String role) { // key may be null (in case we use AdapterKey) in bindings if (role == null) { throw new NullPointerException("Role may not be null."); } return new AdapterKey<>(key == null ? null : TypeToken.of(key), role); } /** * Creates a new {@link AdapterKey} for the given type key and the * {@link #DEFAULT_ROLE} role, which can be used to retrieve an adapter from * an IAdaptable. * * @param <T> * The adapter type. * @param key * The key to use for the newly created {@link AdapterKey}. May * not be <code>null</code>. * @return A new {@link AdapterKey} for the given key and role. * * @see #get(TypeToken, String) */ public static <T> AdapterKey<T> get(TypeToken<T> key) { return get(key, DEFAULT_ROLE); } /** * Creates a new {@link AdapterKey} for the given key and role, which can be * used to retrieve an adapter from an IAdaptable. * * @param <T> * The adapter type. * @param key * The key to use for the newly created {@link AdapterKey}. May * not be <code>null</code>. * @param role * The role to use for the newly created {@link AdapterKey}. May * not be <code>null</code>. * @return A new {@link AdapterKey} for the given key and role. */ public static <T> AdapterKey<T> get(TypeToken<T> key, String role) { // key may be null (in case we use AdapterKey) in bindings if (role == null) { throw new NullPointerException("Role may not be null."); } return new AdapterKey<>(key, role); } /** * Returns an {@link AdapterKey} with no type key and the given role, which * can only be used in adapter map bindings. See {@link AdapterMaps}. * * @param role * The role to use. * * @return An AdapterKey without type key, using the given role. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static AdapterKey<?> role(String role) { return get((Class) null, role); } private TypeToken<T> key; private String role; private AdapterKey(TypeToken<T> typeKey, String role) { this.key = typeKey; this.role = role; } @Override public int compareTo(AdapterKey<T> o) { if (key == null) { throw new IllegalArgumentException( "An AdapterKey that is used for binding cannot be compared."); } // primarily sort by role if (role.equals(o.getRole())) { // secondarily sort by type key return key.toString().compareTo(o.getKey().toString()); } else { return role.compareTo(o.getRole()); } } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } AdapterKey<?> other = (AdapterKey<?>) obj; if (key == null) { // XXX: In case the map binder used for adapter map injection does // not permit duplicates (see MapBinder#permitDuplicates()), we can // access the linked bindings of the adapter map binder to infer the // actual adapter type (when it refers to a constructor binding), // allowing us to omit the adapter type information from the // AdapterKey of the map binding. // // However, in case we are omitting the type key from the // AdapterKey, we have to ensure AdapterKeys without type keys are // never equal to others (because map binder would otherwise detect // duplicates). return false; } else if (!key.equals(other.key)) { return false; } if (role == null) { if (other.role != null) { return false; } } else if (!role.equals(other.role)) { return false; } return true; } /** * Returns the key used by this {@link AdapterKey}. * * @return The key being used. */ public TypeToken<T> getKey() { return key; } /** * Returns the role used by this {@link AdapterKey}. * * @return The role being used. */ public String getRole() { return role; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((key == null) ? 0 : key.hashCode()); result = prime * result + ((role == null) ? 0 : role.hashCode()); return result; } @Override public String toString() { return "AdapterKey(" + key + ", " + role + ")"; } }