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