blob: 4577c96109c3e45e84823347e2a4c769d66c882b [file] [log] [blame]
/*
* 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);
}
}