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