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 gololang.ir;
012
013import java.util.Collections;
014import java.util.LinkedList;
015import java.util.List;
016
017import org.eclipse.golo.compiler.PackageAndClass;
018
019/**
020 * Abstract representation of a Golo invocation in the IR tree.
021 * <p>
022 * Used to represent regular function call and method invocation.
023 * <p>
024 * Note that the called object is only refereed by its name and not an IR node, since the linkage is done at runtime.
025 * There is no guaranty that such an object exists.
026 */
027public abstract class AbstractInvocation<T extends AbstractInvocation<T>> extends ExpressionStatement<T> {
028
029  private PackageAndClass packageAndClass;
030  private final List<GoloElement<?>> arguments = new LinkedList<>();
031  protected boolean usesNamedArguments = false;
032
033  AbstractInvocation(String name) {
034    super();
035    this.packageAndClass = PackageAndClass.of(name);
036  }
037
038  /**
039   * Returns the fully qualified name of the called object.
040   */
041  public String getName() {
042    return packageAndClass.toString();
043  }
044
045  /**
046   * Returns the module part of the called object name.
047   */
048  public String getModuleName() {
049    if (packageAndClass == null) { return ""; }
050    return packageAndClass.packageName();
051  }
052
053  /**
054   * Returns the object part of the called object name.
055   */
056  public String getFunctionName() {
057    if (packageAndClass == null) { return ""; }
058    return packageAndClass.className();
059  }
060
061  public void setPackageAndClass(PackageAndClass name) {
062    this.packageAndClass = name;
063  }
064
065  public PackageAndClass getPackageAndClass() {
066    return this.packageAndClass;
067  }
068
069  private void addArgument(GoloElement<?> argument) {
070    arguments.add(makeParentOf(argument));
071    if (argument instanceof NamedArgument) {
072      withNamedArguments();
073    }
074  }
075
076  /**
077   * Defines the values of the arguments for this invocation.
078   *
079   * <p>This is a builder method.
080   * <p>This methods <em>appends</em> the arguments to the existing ones, so that it can be called multiple times to
081   * create the invocation incrementally. On the other hand, it is <em>not</em> idempotent.
082   * <p>Calls {@link #withNamedArguments()} if needed.
083   *
084   * @param arguments the arguments of the invocation. An argument can be any {@link GoloElement}, or are converted with
085   * {@link ExpressionStatement#of(Object)} otherwise, so that strings and primitives are automatically wrapped in a
086   * {@link ConstantStatement}.
087   * @return this invocation
088   * @see #withNamedArguments()
089   * @see ExpressionStatement#of(Object)
090   */
091  public T withArgs(Object... arguments) {
092    for (Object argument : arguments) {
093      if (argument instanceof GoloElement) {
094        addArgument((GoloElement) argument);
095      } else {
096        addArgument(ExpressionStatement.of(argument));
097      }
098    }
099    return self();
100  }
101
102  public List<GoloElement<?>> getArguments() {
103    return Collections.unmodifiableList(arguments);
104  }
105
106  /**
107   * Returns the number or arguments of this invocation.
108   *
109   * <p>It can be different of called object number of parameters if it is a vararg one.
110   * <p>Since the call is resolved at runtime, there is no guaranty that these two numbers match.
111   */
112  public int getArity() {
113    return arguments.size();
114  }
115
116  /**
117   * Checks if this call uses the named arguments syntax.
118   * <p>See the
119   * <a href="http://golo-lang.org/documentation/next/index.html#_named_parameters">Golo Guide on Named Parameters</a>
120   */
121  public boolean usesNamedArguments() {
122    return usesNamedArguments;
123  }
124
125  public boolean namedArgumentsComplete() {
126    return this.arguments.isEmpty() || this.usesNamedArguments;
127  }
128
129  /**
130   * Mark the invocation as using names arguments syntax.
131   *
132   * <p>This is a builder method.
133   *
134   * <p>This method should not need to be called directly since the {@link #withArgs(Object...)} one checks its argument
135   * and call this one if needed.
136   *
137   * @see #withArgs(Object...)
138   */
139  public T withNamedArguments() {
140    this.usesNamedArguments = true;
141    return self();
142  }
143
144  /**
145   * {@inheritDoc}
146   */
147  @Override
148  public List<GoloElement<?>> children() {
149    return Collections.unmodifiableList(arguments);
150  }
151
152  /**
153   * {@inheritDoc}
154   */
155  @Override
156  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
157    if (arguments.contains(original)) {
158      this.arguments.set(arguments.indexOf(original), newElement);
159      makeParentOf(newElement);
160      if (newElement instanceof NamedArgument) {
161        withNamedArguments();
162      }
163    } else {
164      throw cantReplace(original, newElement);
165    }
166  }
167}