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