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.compiler.ir;
011
012import java.util.Collection;
013import java.util.Set;
014import java.util.LinkedHashSet;
015import java.util.LinkedList;
016
017import org.eclipse.golo.compiler.PackageAndClass;
018import org.eclipse.golo.compiler.parser.GoloASTNode;
019
020import static java.util.Collections.unmodifiableSet;
021
022/**
023 * "classical" augmentation.
024 * <p>
025 * Represents all the augmentations applied to a type, i.e. functions and named augmentations
026 * applied with the {@code with} construct.
027 * <p>
028 * This represents code such
029 * <pre class="listing"><code class="lang-golo" data-lang="golo">
030 * augment MyType {
031 *   function foo = |this| -> ...
032 * }
033 * </code></pre>
034 * or
035 * <pre class="listing"><code class="lang-golo" data-lang="golo">
036 * augment MyType with MyAugmentation
037 * </code></pre>
038 */
039public final class Augmentation extends GoloElement implements FunctionContainer {
040  private PackageAndClass target;
041  private final Set<GoloFunction> functions = new LinkedHashSet<>();
042  private final Set<String> names = new LinkedHashSet<>();
043
044  Augmentation(PackageAndClass target) {
045    super();
046    this.target = target;
047  }
048
049  @Override
050  public Augmentation ofAST(GoloASTNode node) {
051    super.ofAST(node);
052    return this;
053  }
054
055  public PackageAndClass getTarget() {
056    return target;
057  }
058
059  public boolean hasLocalTarget() {
060    return target.packageName().isEmpty();
061  }
062
063  public void setTargetPackage(String packageName) {
064    target = new PackageAndClass(packageName, target.className());
065  }
066
067  @Override
068  public Set<GoloFunction> getFunctions() {
069    return unmodifiableSet(functions);
070  }
071
072  @Override
073  public void addFunction(GoloFunction func) {
074    functions.add(func);
075    makeParentOf(func);
076  }
077
078  @Override
079  public boolean hasFunctions() {
080    return !functions.isEmpty();
081  }
082
083  public Set<String> getNames() {
084    return unmodifiableSet(names);
085  }
086
087  public boolean hasNames() {
088    return !names.isEmpty();
089  }
090
091  public Augmentation with(Object... objects) {
092    return with(java.util.Arrays.asList(objects));
093  }
094
095  public Augmentation with(Collection<?> objects) {
096    if (objects != null) {
097      for (Object o : objects) {
098        if (o instanceof String) {
099          names.add((String) o);
100        } else if (o instanceof GoloFunction) {
101          addFunction((GoloFunction) o);
102        } else {
103          throw cantConvert("string or function", o);
104        }
105      }
106    }
107    return this;
108  }
109
110  public void merge(Augmentation other) {
111    if (!other.getTarget().equals(target)) {
112      throw new IllegalArgumentException("Can't merge augmentations to different targets");
113    }
114    if (other != this) {
115      this.names.addAll(other.getNames());
116      addFunctions(other.getFunctions());
117    }
118  }
119
120  @Override
121  public String toString() {
122    return String.format("Augmentation<target=%s, names=%s, functions=%s>",
123           getTarget(),
124           getNames(),
125           getFunctions());
126  }
127
128  @Override
129  protected void replaceElement(GoloElement original, GoloElement newElement) {
130    if (functions.contains(original) && newElement instanceof GoloFunction) {
131      functions.remove((GoloFunction) original);
132      functions.add((GoloFunction) newElement);
133    } else {
134      throw cantReplace(original, newElement);
135    }
136  }
137
138  @Override
139  public void accept(GoloIrVisitor visitor) {
140    visitor.visitAugmentation(this);
141  }
142
143  @Override
144  public void walk(GoloIrVisitor visitor) {
145    for (GoloFunction func : new LinkedList<GoloFunction>(functions)) {
146      func.accept(visitor);
147    }
148  }
149}