001/* 002 * Copyright (c) 2012-2021 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 gololang.ir; 012 013import java.util.*; 014 015import org.eclipse.golo.compiler.PackageAndClass; 016 017import static java.util.Collections.unmodifiableSet; 018import static gololang.Messages.message; 019 020/** 021 * "classical" augmentation. 022 * <p> 023 * Represents all the augmentations applied to a type, i.e. functions and named augmentations 024 * applied with the {@code with} construct. 025 * <p> 026 * This represents code such 027 * <pre class="listing"><code class="lang-golo" data-lang="golo"> 028 * augment MyType { 029 * function foo = |this| -> ... 030 * } 031 * </code></pre> 032 * or 033 * <pre class="listing"><code class="lang-golo" data-lang="golo"> 034 * augment MyType with MyAugmentation 035 * </code></pre> 036 */ 037public final class Augmentation extends GoloElement<Augmentation> implements FunctionContainer, ToplevelGoloElement { 038 private final PackageAndClass target; 039 private final Set<GoloFunction> functions = new LinkedHashSet<>(); 040 private final Set<MacroInvocation> macroCalls = new LinkedHashSet<>(); 041 private final Set<String> names = new LinkedHashSet<>(); 042 043 private Augmentation(PackageAndClass target) { 044 super(); 045 this.target = target; 046 } 047 048 protected Augmentation self() { return this; } 049 050 /** 051 * Creates an augmentation on the target name. 052 * 053 * @param target the name of the target (compatible with {@link PackageAndClass#of(Object)}) 054 * @return a classical augmentation 055 * @see PackageAndClass#of(Object) 056 */ 057 public static Augmentation of(Object target) { 058 return new Augmentation(PackageAndClass.of(target)); 059 } 060 061 /** 062 * Returns the target type of this augmentation. 063 * 064 * <p>Note that since resolution is done at runtime, the target is only referenced by its name (here a 065 * {@link PackageAndClass}). 066 */ 067 public PackageAndClass getTarget() { 068 if (target.packageName().isEmpty()) { 069 GoloModule mod = enclosingModule(); 070 if (mod != null) { 071 return mod.getTypesPackage().createSubPackage(target.className()); 072 } 073 } 074 return target; 075 } 076 077 @Override 078 public PackageAndClass getPackageAndClass() { 079 return getTarget(); 080 } 081 082 /** 083 * {@inheritDoc} 084 */ 085 @Override 086 public List<GoloFunction> getFunctions() { 087 return new ArrayList<>(functions); 088 } 089 090 /** 091 * {@inheritDoc} 092 */ 093 @Override 094 public void addFunction(GoloFunction func) { 095 if (func.getArity() == 0) { 096 throw new IllegalArgumentException(message("augment_function_no_args", func.getName(), this.getPackageAndClass())); 097 } 098 functions.add(makeParentOf(func)); 099 } 100 101 /** 102 * {@inheritDoc} 103 */ 104 @Override 105 public void addMacroInvocation(MacroInvocation macroCall) { 106 macroCalls.add(macroCall); 107 makeParentOf(macroCall); 108 } 109 110 /** 111 * {@inheritDoc} 112 */ 113 @Override 114 public boolean hasFunctions() { 115 return !functions.isEmpty(); 116 } 117 118 /** 119 * Returns the names of the applied named augmentations. 120 */ 121 public Set<String> getNames() { 122 return unmodifiableSet(names); 123 } 124 125 /** 126 * Checks if named augmentations were applied. 127 */ 128 public boolean hasNames() { 129 return !names.isEmpty(); 130 } 131 132 /** 133 * Define the functions or named augmentations to add to the target. 134 * <p> 135 * The objects to add can be a function or a string representing the name of a named 136 * augmentation. 137 * 138 * <p>This is a builder method. 139 * 140 * @param objects functions or named augmentations to add 141 * @return this augmentation 142 */ 143 public Augmentation with(Object... objects) { 144 return with(java.util.Arrays.asList(objects)); 145 } 146 147 /** 148 * Define the functions or named augmentations to add to the target. 149 * <p> 150 * The objects to add can be a function or a string representing the name of a named 151 * augmentation. 152 * 153 * <p>This is a builder method. 154 * 155 * @param objects a collection of functions or named augmentations to add 156 * @return this augmentation 157 */ 158 public Augmentation with(Collection<?> objects) { 159 if (objects != null) { 160 for (Object o : objects) { 161 if (o instanceof String) { 162 names.add((String) o); 163 } else { 164 addElement(o); 165 } 166 } 167 } 168 return this; 169 } 170 171 /** 172 * Merge two augmentations applied to the same target. 173 * 174 * <p><strong>Warning:</strong>the functions contained in the other augmentation being <em>move</em> to this one and <em>not copied</em>, 175 * the other augmentation references functions but is no more their parent. Therefore, the other augmentation is not 176 * in a consistent state and should be dropped, since no more needed. 177 */ 178 public void merge(Augmentation other) { 179 if (!other.getTarget().equals(getTarget())) { 180 throw new IllegalArgumentException("Can't merge augmentations to different targets"); 181 } 182 if (other != this) { 183 this.names.addAll(other.getNames()); 184 addFunctions(other.getFunctions()); 185 } 186 } 187 188 @Override 189 public String toString() { 190 return String.format("Augmentation<target=%s, names=%s, functions=%s>", 191 getTarget(), 192 getNames(), 193 getFunctions()); 194 } 195 196 /** 197 * {@inheritDoc} 198 */ 199 @Override 200 protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) { 201 if (functions.contains(original)) { 202 functions.remove(original); 203 } else if (macroCalls.contains(original)) { 204 macroCalls.remove(original); 205 } else { 206 throw cantReplace(original, newElement); 207 } 208 addElement(newElement); 209 } 210 211 /** 212 * {@inheritDoc} 213 */ 214 @Override 215 public void accept(GoloIrVisitor visitor) { 216 visitor.visitAugmentation(this); 217 } 218 219 /** 220 * {@inheritDoc} 221 */ 222 @Override 223 public List<GoloElement<?>> children() { 224 LinkedList<GoloElement<?>> children = new LinkedList<>(functions); 225 children.addAll(macroCalls); 226 return children; 227 } 228}