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}