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 org.eclipse.golo.runtime.Loader; 014import org.eclipse.golo.runtime.Module; 015 016import java.util.function.Predicate; 017import java.util.stream.Stream; 018 019import static org.eclipse.golo.runtime.augmentation.AugmentationApplication.Kind; 020 021/** 022 * Encapsulate a module defining an augmentation. 023 */ 024public final class DefiningModule { 025 026 public enum Scope { LOCAL, IMPORT, CALLSTACK } 027 028 private final Class<?> module; 029 private final Scope scope; 030 031 DefiningModule(Class<?> module, Scope scope) { 032 this.module = module; 033 this.scope = scope; 034 } 035 036 public Class<?> module() { 037 return this.module; 038 } 039 040 public static DefiningModule of(Class<?> module, Scope scope) { 041 return new DefiningModule(module, scope); 042 } 043 044 public static DefiningModule ofLocal(Class<?> module) { 045 return new DefiningModule(module, Scope.LOCAL); 046 } 047 048 public static DefiningModule ofImport(Class<?> module) { 049 return new DefiningModule(module, Scope.IMPORT); 050 } 051 052 public static DefiningModule ofCallstack(Class<?> module) { 053 return new DefiningModule(module, Scope.CALLSTACK); 054 } 055 056 /** 057 * Returns a stream of augmentations definitions defined directly on a type in the corresponding 058 * module. 059 * e.g. 060 * <pre class="listing"><code class="lang-golo" data-lang="golo"> 061 * augment module.Type { 062 * # ... 063 * } 064 * </code></pre> 065 */ 066 private Stream<AugmentationApplication> simpleAugmentationsFor(Loader loader, Class<?> receiverType) { 067 return Stream.of(Module.augmentations(module)) 068 .map(loader) 069 .filter(isAssignableFrom(receiverType)) 070 .map(target -> new AugmentationApplication( 071 loader.load(module.getName() + "$" + target.getName().replace(".", "$")), 072 target, scope, Kind.SIMPLE)); 073 } 074 075 private static Predicate<Class<?>> isAssignableFrom(Class<?> receiver) { 076 return target -> target != null && target.isAssignableFrom(receiver); 077 } 078 079 private Stream<AugmentationApplication> fullyNamedAugmentationsFor(Loader loader, Class<?> receiverType) { 080 return Stream.of(Module.augmentationApplications(module)) 081 .map(loader::load) 082 .filter(target -> target != null && target.isAssignableFrom(receiverType)) 083 .flatMap(target -> qualifyAugmentations(loader, target)); 084 } 085 086 private Stream<AugmentationApplication> qualifyAugmentations(Loader loader, Class<?> target) { 087 return Stream.of(Module.augmentationApplications(module, target)) 088 .flatMap(this::fullyQualifiedName) 089 .map(augmentName -> new AugmentationApplication( 090 loader.load(augmentName), 091 target, scope, Kind.NAMED)); 092 } 093 094 /** 095 * Fully qualify an augmentation name. 096 * <p> 097 * Given an augmentation name, this generate a stream of alternative names by prepending 098 * names of the defining module as well as imported modules. 099 */ 100 private Stream<String> fullyQualifiedName(String augmentationName) { 101 Stream.Builder<String> names = Stream.builder(); 102 int idx = augmentationName.lastIndexOf("."); 103 if (idx == -1) { 104 names.add(augmentationName); 105 } else { 106 names.add(new StringBuilder(augmentationName).replace(idx, idx + 1, "$").toString()); 107 } 108 names.add(module.getName() + "$" + augmentationName.replace(".", "$")); 109 return Stream.concat( 110 names.build(), 111 Stream.of(Module.imports(module)) 112 .map(prefix -> prefix + "$" + augmentationName.replace(".", "$"))); 113 } 114 115 public Stream<AugmentationApplication> augmentationsFor(Loader loader, Class<?> receiverType) { 116 if (module == null) { 117 return Stream.empty(); 118 } 119 return Stream.concat( 120 simpleAugmentationsFor(loader, receiverType), 121 fullyNamedAugmentationsFor(loader, receiverType)); 122 123 } 124 125 @Override 126 public String toString() { 127 return "DefiningModule<" + module + "," + scope + ">"; 128 } 129} 130 131