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}