001/*
002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package org.eclipse.golo.runtime.augmentation;
012
013import java.lang.reflect.Method;
014import java.util.Objects;
015
016/**
017 * Encapsulate runtime information about augmentation method resolution.
018 */
019public final class AugmentationMethod implements Comparable<AugmentationMethod> {
020
021  private final AugmentationApplication.Kind kind;
022  private final DefiningModule.Scope scope;
023  private final Class<?> target;
024  private final Method method;
025
026  AugmentationMethod(AugmentationApplication.Kind kind, DefiningModule.Scope scope, Class<?> target, Method method) {
027    this.kind = kind;
028    this.scope = scope;
029    this.target = target;
030    this.method = method;
031  }
032
033  public Method method() {
034    return method;
035  }
036
037  @Override
038  public String toString() {
039    return String.format("AugmentationMethod<%s,%s,%s,%s>",
040        kind, scope, target, method);
041  }
042
043  /**
044   * Compare applications for priority.
045   * <p>
046   * The greater application is the one with the lower priority so that in a stable-sorted list of
047   * application, we always take the first one.
048   * <p>
049   * The natural order defined is:
050   * {@code target specificity < scope < kind}
051   * so that the implied priority is to use the augmentation defined:
052   * <ol>
053   * <li> on the more specific class: to allow to override augmentations by target specificity,
054   * similar to method overriding;
055   * <li> in the more local scope: to allow to override augmentations by defining them locally;
056   * <li> by direct definition instead of named application: to allow to augment a class with a
057   * named augmentation (mixin-like) but override some of the methods by directly defining them;
058   * <li> before any other augmentation: to control augmentation application using the import or
059   * definition order.
060   * </ol>
061   * Moreover, augmentation method with fixed arity are used in preference to variable-arity ones.
062   */
063  @Override
064  public int compareTo(AugmentationMethod other) {
065    if (this.target.isAssignableFrom(other.target) && !this.target.equals(other.target)) {
066      return 1;
067    }
068    if (other.target.isAssignableFrom(this.target) && !this.target.equals(other.target)) {
069      return -1;
070    }
071    if (this.scope != other.scope) {
072      return this.scope.compareTo(other.scope);
073    }
074    if (this.kind != other.kind) {
075      return this.kind.compareTo(other.kind);
076    }
077    if (this.method.isVarArgs() && !other.method.isVarArgs()) {
078      return 1;
079    }
080    if (other.method.isVarArgs() && !this.method.isVarArgs()) {
081      return -1;
082    }
083    return 0;
084  }
085
086  @Override
087  public boolean equals(Object o) {
088    if (o == null) { return false; }
089    if (this == o) { return true; }
090    if (!this.getClass().equals(o.getClass())) { return false; }
091    AugmentationMethod that = (AugmentationMethod) o;
092    return this.kind == that.kind
093      && this.scope == that.scope
094      && this.target.equals(that.target)
095      && this.method.equals(that.method);
096  }
097
098  @Override
099  public int hashCode() {
100    return Objects.hash(kind, scope, target, method);
101  }
102}
103