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 org.eclipse.golo.compiler.parser.GoloASTNode; 013 014import java.lang.ref.WeakReference; 015import java.util.Optional; 016import java.util.NoSuchElementException; 017 018public abstract class GoloElement { 019 private WeakReference<GoloASTNode> nodeRef; 020 private Optional<GoloElement> parent = Optional.empty(); 021 private String documentation; 022 023 public void setASTNode(GoloASTNode node) { 024 if (node != null) { 025 nodeRef = new WeakReference<>(node); 026 setDocumentationFrom(node); 027 } 028 } 029 030 public GoloASTNode getASTNode() { 031 if (nodeRef == null) { return null; } 032 return nodeRef.get(); 033 } 034 035 public boolean hasASTNode() { 036 return nodeRef != null && nodeRef.get() != null; 037 } 038 039 public GoloElement ofAST(GoloASTNode node) { 040 if (node != null) { 041 node.setIrElement(this); 042 setDocumentationFrom(node); 043 } 044 return this; 045 } 046 047 private void setDocumentationFrom(GoloASTNode node) { 048 if (node != null && node.getDocumentation() != null) { 049 documentation = node.getDocumentation(); 050 } 051 } 052 053 protected void setParentNode(GoloElement parentElement) { 054 this.parent = Optional.ofNullable(parentElement); 055 } 056 057 public Optional<GoloElement> getParentNode() { 058 return this.parent; 059 } 060 061 public void makeParentOf(GoloElement childElement) { 062 if (childElement != null) { 063 childElement.setParentNode(this); 064 if (childElement instanceof Scope) { 065 Optional<ReferenceTable> referenceTable = this.getLocalReferenceTable(); 066 if (referenceTable.isPresent()) { 067 ((Scope) childElement).relink(referenceTable.get()); 068 } 069 } 070 } 071 } 072 073 protected RuntimeException cantReplace() { 074 return new UnsupportedOperationException(getClass().getName() + " can't replace elements"); 075 } 076 077 protected RuntimeException cantReplace(GoloElement original, GoloElement replacement) { 078 return new IllegalArgumentException(this + " can't replace " + original + " with " + replacement); 079 } 080 081 protected RuntimeException doesNotContain(GoloElement element) { 082 return new NoSuchElementException(element + " not in " + this); 083 } 084 085 protected static RuntimeException cantConvert(String expected, Object value) { 086 return new ClassCastException("expecting a " + expected + "but got a " + value.getClass()); 087 } 088 089 public void replaceInParentBy(GoloElement newElement) { 090 if (this.parent.isPresent()) { 091 this.parent.get().replaceElement(this, newElement); 092 this.parent.get().makeParentOf(newElement); 093 if (hasASTNode()) { 094 getASTNode().setIrElement(newElement); 095 } 096 this.setParentNode(null); 097 } 098 } 099 100 public String getDocumentation() { 101 return documentation; 102 } 103 104 public PositionInSourceCode getPositionInSourceCode() { 105 if (hasASTNode()) { 106 return getASTNode().getPositionInSourceCode(); 107 } 108 return new PositionInSourceCode(0, 0); 109 } 110 111 public Optional<ReferenceTable> getLocalReferenceTable() { 112 if (parent.isPresent()) { 113 return parent.get().getLocalReferenceTable(); 114 } 115 return Optional.empty(); 116 } 117 118 /** 119 * Accept the visitor. 120 * <p> 121 * This method should only call the visitor {@code visitXXXX} method. 122 * The children of this node will be visited by the 123 * {@link #walk(GoloIrVisitor)} method. 124 */ 125 public abstract void accept(GoloIrVisitor visitor); 126 127 /** 128 * Walk the visitor through this node children. 129 */ 130 public abstract void walk(GoloIrVisitor visitor); 131 132 /** 133 * Replace a child. 134 * <p> 135 * Replace {@code original} with {@code newElement} if {@code original} is a child of this node 136 * and type matches. 137 * 138 * @param original the original value to replace. Must be a child of this node 139 * @param newElement the element to replace with. Type must match. 140 * @throws UnsupportedOperationException if this node does not support replacement 141 * @throws NoSuchElementException if {@code original} is not a child of this node 142 * @throws ClassCastException if the type of {@code newElement} does not match 143 * @see #cantReplace() 144 * @see #cantReplace(GoloElement, GoloElement) 145 * @see #doesNotContain(GoloElement) 146 * @see #cantConvert(String, Object) 147 */ 148 protected abstract void replaceElement(GoloElement original, GoloElement newElement); 149 150}