001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.jjs.ast;
017:
018: import com.google.gwt.dev.jjs.InternalCompilerException;
019:
020: import java.util.ArrayList;
021: import java.util.List;
022:
023: /**
024: * A visitor for iterating through and modifying an AST.
025: */
026: public class JModVisitor extends JVisitor {
027:
028: private interface ContextFactory {
029: Context create();
030: }
031:
032: private class ContextPool extends ArrayList {
033:
034: private ContextFactory factory;
035: private int pos = 0;
036:
037: public ContextPool(ContextFactory factory) {
038: this .factory = factory;
039: }
040:
041: public void release(Context ctx) {
042: if (get(--pos) != ctx) {
043: throw new InternalCompilerException(
044: "Tried to release the wrong context");
045: }
046: }
047:
048: public Context take() {
049: if (pos == size()) {
050: add(factory.create());
051: }
052: return (Context) get(pos++);
053: }
054: }
055:
056: private class ListContext implements Context {
057: private int index;
058: private List list;
059: private boolean removed;
060: private boolean replaced;
061:
062: public boolean canInsert() {
063: return true;
064: }
065:
066: public boolean canRemove() {
067: return true;
068: }
069:
070: public void insertAfter(JNode node) {
071: checkRemoved();
072: list.add(index + 1, node);
073: didChange = true;
074: }
075:
076: public void insertBefore(JNode node) {
077: checkRemoved();
078: list.add(index++, node);
079: didChange = true;
080: }
081:
082: public void removeMe() {
083: checkState();
084: list.remove(index--);
085: didChange = removed = true;
086: }
087:
088: public void replaceMe(JNode node) {
089: checkState();
090: checkReplacement((JNode) list.get(index), node);
091: list.set(index, node);
092: didChange = replaced = true;
093: }
094:
095: protected void doReplace(Class targetClass, JNode x) {
096: checkState();
097: checkReplacement((JNode) list.get(index), x);
098: list.set(index, x);
099: didChange = replaced = true;
100: }
101:
102: protected void traverse(List list) {
103: this .list = list;
104: for (index = 0; index < list.size(); ++index) {
105: removed = replaced = false;
106: doTraverse((JNode) list.get(index), this );
107: }
108: }
109:
110: private void checkRemoved() {
111: if (removed) {
112: throw new InternalCompilerException(
113: "Node was already removed");
114: }
115: }
116:
117: private void checkState() {
118: checkRemoved();
119: if (replaced) {
120: throw new InternalCompilerException(
121: "Node was already replaced");
122: }
123: }
124: }
125:
126: private class NodeContext implements Context {
127: private JNode node;
128: private boolean replaced;
129:
130: public boolean canInsert() {
131: return false;
132: }
133:
134: public boolean canRemove() {
135: return false;
136: }
137:
138: public void insertAfter(JNode node) {
139: throw new UnsupportedOperationException();
140: }
141:
142: public void insertBefore(JNode node) {
143: throw new UnsupportedOperationException();
144: }
145:
146: public void removeMe() {
147: throw new UnsupportedOperationException();
148: }
149:
150: public void replaceMe(JNode node) {
151: if (replaced) {
152: throw new InternalCompilerException(
153: "Node was already replaced");
154: }
155: checkReplacement(this .node, node);
156: this .node = node;
157: didChange = replaced = true;
158: }
159:
160: protected JNode traverse(JNode node) {
161: this .node = node;
162: replaced = false;
163: doTraverse(node, this );
164: return this .node;
165: }
166: }
167:
168: protected static void checkReplacement(JNode origNode, JNode newNode) {
169: if (newNode == null) {
170: throw new InternalCompilerException(
171: "Cannot replace with null");
172: }
173: if (newNode == origNode) {
174: throw new InternalCompilerException(
175: "The replacement is the same as the original");
176: }
177: }
178:
179: protected boolean didChange = false;
180:
181: private final ContextPool listContextPool = new ContextPool(
182: new ContextFactory() {
183: public Context create() {
184: return new ListContext();
185: }
186: });
187:
188: private final ContextPool nodeContextPool = new ContextPool(
189: new ContextFactory() {
190: public Context create() {
191: return new NodeContext();
192: }
193: });
194:
195: public boolean didChange() {
196: return didChange;
197: }
198:
199: protected JNode doAccept(JNode node) {
200: NodeContext ctx = (NodeContext) nodeContextPool.take();
201: try {
202: return ctx.traverse(node);
203: } finally {
204: nodeContextPool.release(ctx);
205: }
206: }
207:
208: protected void doAccept(List list) {
209: NodeContext ctx = (NodeContext) nodeContextPool.take();
210: try {
211: for (int i = 0, c = list.size(); i < c; ++i) {
212: list.set(i, ctx.traverse((JNode) list.get(i)));
213: }
214: } finally {
215: nodeContextPool.release(ctx);
216: }
217: }
218:
219: protected void doAcceptWithInsertRemove(List list) {
220: ListContext ctx = (ListContext) listContextPool.take();
221: try {
222: ctx.traverse(list);
223: } finally {
224: listContextPool.release(ctx);
225: }
226: }
227:
228: }
|