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