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.Objects;
014import java.util.LinkedList;
015import java.util.List;
016
017/**
018 * A {@code try catch finally} statement.
019 *
020 * <p>Represents nodes such as:
021 * <pre class="listing"><code class="lang-golo" data-lang="golo">
022 * try {
023 *   # try block
024 * } catch (ex) {
025 *   # catch block
026 * } finally {
027 *   # finally block
028 * }
029 * </code></pre>
030 */
031public final class TryCatchFinally extends GoloStatement<TryCatchFinally> {
032
033  private String exceptionId;
034  private Block tryBlock;
035  private Block catchBlock;
036  private Block finallyBlock;
037
038  private TryCatchFinally(String exceptionId) {
039    super();
040    this.exceptionId = exceptionId;
041  }
042
043  /**
044   * Creates an empty {@code try catch finally} statement.
045   */
046  public static TryCatchFinally tryCatch() {
047    return new TryCatchFinally(null);
048  }
049
050
051  /**
052   * Creates a complete {@code try catch finally} statement.
053   * <p>
054   * Less readable than the fluent API, but useful for meta-generation.
055   */
056  public static TryCatchFinally create(String exceptionName, GoloElement<?> tryBlock, GoloElement<?> catchBlock, GoloElement<?> finallyBlock) {
057    return new TryCatchFinally(null)
058      .trying(tryBlock)
059      .catching(exceptionName, catchBlock)
060      .finalizing(finallyBlock);
061  }
062
063  protected TryCatchFinally self() { return this; }
064
065  /**
066   * Get the exception name.
067   *
068   * <p>{@code ex} in the previous example.
069   */
070  public String getExceptionId() {
071    return exceptionId;
072  }
073
074  public Block getTryBlock() {
075    return tryBlock;
076  }
077
078  /**
079   * Defines the try block.
080   *
081   * <p>This is a builder method.
082   *
083   * @param block a {@link Block} or a statement that will be wrapped.
084   * @see Block#of(Object)
085   */
086  public TryCatchFinally trying(Object block) {
087    this.tryBlock = makeParentOf(Block.of(block));
088    return this;
089  }
090
091  public Block getCatchBlock() {
092    return catchBlock;
093  }
094
095  /**
096   * Defines the catch block.
097   *
098   * <p>This is a builder method.
099   *
100   * @param exceptionId the name of the catched exception instance.
101   * @param block a {@link Block} or a statement that will be wrapped.
102   * @see Block#of(Object)
103   */
104  public TryCatchFinally catching(String exceptionId, Object block) {
105    this.exceptionId = exceptionId;
106    return catching(block);
107  }
108
109  public TryCatchFinally catching(Object block) {
110    if (block == null) {
111      this.catchBlock = null;
112      this.exceptionId = null;
113      return this;
114    }
115    this.catchBlock = makeParentOf(Block.of(block));
116    this.catchBlock.getReferenceTable().add(LocalReference.of(exceptionId).synthetic());
117    return this;
118  }
119
120  public Block getFinallyBlock() {
121    return finallyBlock;
122  }
123
124  /**
125   * Defines the finally block.
126   *
127   * <p>This is a builder method.
128   *
129   * @param block a {@link Block} or a statement that will be wrapped.
130   * @see Block#of(Object)
131   */
132  public TryCatchFinally finalizing(Object block) {
133    this.finallyBlock = makeParentOf(Block.of(block));
134    return this;
135  }
136
137  public boolean hasFinallyBlock() {
138    return finallyBlock != null;
139  }
140
141  public boolean hasCatchBlock() {
142    return catchBlock != null;
143  }
144
145  /**
146   * Check if this statement has both a catch block and a finally block.
147   */
148  public boolean isTryCatchFinally() {
149    return hasCatchBlock() && hasFinallyBlock();
150  }
151
152  /**
153   * Check if this statement has only a catch block.
154   */
155  public boolean isTryCatch() {
156    return hasCatchBlock() && !hasFinallyBlock();
157  }
158
159  /**
160   * Check if this statement has only a finally block.
161   */
162  public boolean isTryFinally() {
163    return !hasCatchBlock() && hasFinallyBlock();
164  }
165
166  /**
167   * {@inheritDoc}
168   */
169  @Override
170  public void accept(GoloIrVisitor visitor) {
171    visitor.visitTryCatchFinally(this);
172  }
173
174  /**
175   * {@inheritDoc}
176   */
177  @Override
178  public List<GoloElement<?>> children() {
179    LinkedList<GoloElement<?>> children = new LinkedList<>();
180    children.add(tryBlock);
181    if (catchBlock != null) {
182      children.add(catchBlock);
183    }
184    if (finallyBlock != null) {
185      children.add(finallyBlock);
186    }
187    return children;
188  }
189
190  /**
191   * {@inheritDoc}
192   */
193  @Override
194  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
195    if (Objects.equals(original, tryBlock)) {
196      trying(newElement);
197    } else if (Objects.equals(original, catchBlock)) {
198      catching(newElement);
199    } else if (Objects.equals(original, finallyBlock)) {
200      finalizing(newElement);
201    } else {
202      throw cantReplace(original, newElement);
203    }
204  }
205}