| /* |
| * Copyright 2008 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.dev.js.ast; |
| |
| import com.google.gwt.dev.jjs.InternalCompilerException; |
| |
| import java.util.List; |
| |
| /** |
| * A visitor for iterating through and modifying an AST. |
| */ |
| public class JsModVisitor extends JsVisitor { |
| |
| @SuppressWarnings("unchecked") |
| private class ListContext<T extends JsVisitable> implements JsContext { |
| private List<T> collection; |
| private int index; |
| private boolean removed; |
| private boolean replaced; |
| |
| public boolean canInsert() { |
| return true; |
| } |
| |
| public boolean canRemove() { |
| return true; |
| } |
| |
| public void insertAfter(JsVisitable node) { |
| checkRemoved(); |
| collection.add(index + 1, (T) node); |
| didChange = true; |
| } |
| |
| public void insertBefore(JsVisitable node) { |
| checkRemoved(); |
| collection.add(index++, (T) node); |
| didChange = true; |
| } |
| |
| public boolean isLvalue() { |
| return false; |
| } |
| |
| public void removeMe() { |
| checkState(); |
| collection.remove(index--); |
| didChange = removed = true; |
| } |
| |
| public void replaceMe(JsVisitable node) { |
| checkState(); |
| checkReplacement(collection.get(index), node); |
| collection.set(index, (T) node); |
| didChange = replaced = true; |
| } |
| |
| protected void traverse(List<T> collection) { |
| this.collection = collection; |
| for (index = 0; index < collection.size(); ++index) { |
| removed = replaced = false; |
| doTraverse(collection.get(index), this); |
| } |
| } |
| |
| private void checkRemoved() { |
| if (removed) { |
| throw new InternalCompilerException("Node was already removed"); |
| } |
| } |
| |
| private void checkState() { |
| checkRemoved(); |
| if (replaced) { |
| throw new InternalCompilerException("Node was already replaced"); |
| } |
| } |
| } |
| |
| private class LvalueContext extends NodeContext<JsExpression> { |
| @Override |
| public boolean isLvalue() { |
| return true; |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private class NodeContext<T extends JsVisitable> implements JsContext { |
| private T node; |
| private boolean replaced; |
| |
| public boolean canInsert() { |
| return false; |
| } |
| |
| public boolean canRemove() { |
| return false; |
| } |
| |
| public void insertAfter(JsVisitable node) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void insertBefore(JsVisitable node) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public boolean isLvalue() { |
| return false; |
| } |
| |
| public void removeMe() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void replaceMe(JsVisitable node) { |
| if (replaced) { |
| throw new InternalCompilerException("Node was already replaced"); |
| } |
| checkReplacement(this.node, node); |
| this.node = (T) node; |
| didChange = replaced = true; |
| } |
| |
| protected T traverse(T node) { |
| this.node = node; |
| replaced = false; |
| doTraverse(node, this); |
| return this.node; |
| } |
| } |
| |
| protected static void checkReplacement(JsVisitable origNode, |
| JsVisitable newNode) { |
| if (newNode == null) { |
| throw new InternalCompilerException("Cannot replace with null"); |
| } |
| if (newNode == origNode) { |
| throw new InternalCompilerException( |
| "The replacement is the same as the original"); |
| } |
| } |
| |
| protected boolean didChange = false; |
| |
| @Override |
| public boolean didChange() { |
| return didChange; |
| } |
| |
| @Override |
| protected <T extends JsVisitable> T doAccept(T node) { |
| return new NodeContext<T>().traverse(node); |
| } |
| |
| @Override |
| protected <T extends JsVisitable> void doAcceptList(List<T> collection) { |
| NodeContext<T> ctx = new NodeContext<T>(); |
| for (int i = 0, c = collection.size(); i < c; ++i) { |
| ctx.traverse(collection.get(i)); |
| if (ctx.replaced) { |
| collection.set(i, ctx.node); |
| } |
| } |
| } |
| |
| @Override |
| protected JsExpression doAcceptLvalue(JsExpression expr) { |
| return new LvalueContext().traverse(expr); |
| } |
| |
| @Override |
| protected <T extends JsVisitable> void doAcceptWithInsertRemove( |
| List<T> collection) { |
| new ListContext<T>().traverse(collection); |
| } |
| |
| } |