View Javadoc

1   /*
2    * BasicListUpdateDelegate.java
3    * 
4    * Copyright (c) 2006 David Holroyd
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package uk.co.badgersinfoil.metaas.impl.antlr;
20  
21  import org.asdt.core.internal.antlr.AS3Parser;
22  import uk.co.badgersinfoil.metaas.impl.ASTUtils;
23  import uk.co.badgersinfoil.metaas.impl.TokenBuilder;
24  
25  /**
26   * Manages the tokens of the parent tree in the most basic way possible, simply
27   * inserting the run of tokens belonging to the child into the run of tokens
28   * belonging to the parent and updating start/stop tokens for the parent if
29   * required.
30   */
31  public class BasicListUpdateDelegate implements TreeTokenListUpdateDelegate {
32  
33  	// TODO: delete PLACEHOLDER tokens when they are superseded by the addition of real tokens
34  
35  	public void addedChild(LinkedListTree parent, LinkedListTree child) {
36  		if (isPlaceholder(parent)) {
37  			if (isPlaceholder(child)) {
38  				throw new IllegalArgumentException("The parent node ("+ASTUtils.tokenName(parent)+") has only a placeholder token, so a child which also has only a placeholder token ("+ASTUtils.tokenName(child)+") can't be added yet");
39  			}
40  			LinkedListToken placeholder = parent.getStartToken();
41  			if (placeholder.getPrev() != null) {
42  				placeholder.getPrev().setNext(child.getStartToken());
43  			}
44  			if (placeholder.getNext() != null) {
45  				placeholder.getNext().setPrev(child.getStopToken());
46  			}
47  			parent.setStartToken(child.getStartToken());
48  			parent.setStopToken(child.getStopToken());
49  			return;
50  		}
51  		LinkedListToken stop = findTokenInsertionPointForChildWithinParent(parent, child);
52  		if (parent.getStartToken() == null) {
53  			parent.setStartToken(child.getStartToken());
54  		}
55  		if (stop != null) {
56  			insertAfter(stop, stop.getNext(),
57  			            child.getStartToken(), child.getStopToken());
58  		}
59  		if (child.getStopToken() != null) {
60  			parent.setStopToken(child.getStopToken());
61  		}
62  	}
63  
64  	private boolean isPlaceholder(LinkedListTree ast) {
65  		return ast.getStartToken()==ast.getStopToken()
66  		    && ast.getStartToken()!=null
67  		    && ast.getStartToken().getType()==AS3Parser.VIRTUAL_PLACEHOLDER
68  		    && ((PlaceholderLinkedListToken)ast.getStartToken()).getHeld()==ast;
69  	}
70  
71  	private LinkedListToken findTokenInsertionPointForChildWithinParent(LinkedListTree parent, LinkedListTree child) {
72  		// FIXME: this fails to take into account am ancestor not
73  		// having the same kind of TreeTokenListUpdateDelegate
74  		while (parent != null) {
75  			if (parent.getChildCount() == 1) {
76  				// the just-added child is the only child of 'parent'
77  				if (parent.getStopToken() != null) {
78  					return parent.getStopToken();
79  				}
80  				if (parent.getStartToken() != null) {
81  					return parent.getStartToken();
82  				}
83  			}
84  			int index = parent.getIndexOfChild(child);
85  			if (index > 0 && index < parent.getChildCount()-1) {
86  				// 'child' is not the *first* child of 'parent'
87  				LinkedListTree precedent = (LinkedListTree)parent.getChild(index-1);
88  				if (precedent.getStopToken() == null) {
89  					// TODO: loop, rather than recurse,
90  					return findTokenInsertionPointForChildWithinParent(parent, precedent);
91  				}
92  				return precedent.getStopToken();
93  			}
94  			if (index==0 && parent.getStartToken()!=null) {
95  				return parent.getStartToken();
96  			}
97  			if (parent.getStopToken() != null) {
98  				return parent.getStopToken();
99  			}
100 			child = parent;
101 			parent = parent.getParent();
102 		}
103 		return null;
104 	}
105 
106 	public void addedChild(LinkedListTree parent, int index, LinkedListTree child) {
107 		LinkedListToken target;
108 		LinkedListToken targetNext;
109 		if (index == 0) {
110 			LinkedListTree prevFirstChild = (LinkedListTree)parent.getChild(1);
111 			targetNext = prevFirstChild.getStartToken();
112 			target = targetNext.getPrev();
113 			if (targetNext == parent.getStartToken()) {
114 				parent.setStartToken(child.getStartToken());
115 			}
116 		} else {
117 			target = ((LinkedListTree)parent.getChild(index - 1)).getStopToken();
118 			targetNext = target.getNext();
119 		}
120 		insertAfter(target, targetNext,
121 		            child.getStartToken(), child.getStopToken());
122 	}
123 
124 	protected static void insertAfter(LinkedListToken target, LinkedListToken targetNext,
125 	                                LinkedListToken start, LinkedListToken stop)
126 	{
127 		if (target == null && targetNext == null) {
128 			throw new IllegalArgumentException("At least one of target and targetNext must be non-null");
129 		}
130 		if (start != null) {
131 //			if (start.getPrev() != null || stop.getNext() != null) {
132 //				throw new IllegalArgumentException("insertAfter("+target+", "+targetNext+", "+start+", "+stop+") : start.getPrev()="+start.getPrev()+" stop.getNext()="+stop.getNext());
133 //			}
134 			// i.e. we're not adding an imaginary node that currently
135 			//      has no real children
136 			if (target != null) {
137 				target.setNext(start);
138 			}
139 			stop.setNext(targetNext);
140 			if (targetNext != null) {
141 				targetNext.setPrev(stop);
142 			}
143 		}
144 	}
145 
146 	public void appendToken(LinkedListTree parent, LinkedListToken append) {
147 		if (parent.getStopToken() == null) {
148 			parent.setStartToken(append);
149 			parent.setStopToken(append);
150 		} else {
151 			// TODO: can this be simplified now?
152 			append.setNext(parent.getStopToken().getNext());
153 			parent.getStopToken().setNext(append);
154 			append.setPrev(parent.getStopToken());
155 			parent.setStopToken(append);
156 		}
157 	}
158 
159 	public void addToken(LinkedListTree parent, int index, LinkedListToken append) {
160 		if (isPlaceholder(parent)) {
161 			LinkedListToken placeholder = parent.getStartToken();
162 			parent.setStartToken(append);
163 			parent.setStopToken(append);
164 			placeholder.setPrev(null);
165 			placeholder.setNext(null);
166 		}
167 		if (parent.getStopToken() == null) {
168 			parent.setStartToken(append);
169 			parent.setStopToken(append);
170 		} else {
171 			LinkedListToken target;
172 			LinkedListToken targetNext;
173 			if (index == 0) {
174 				targetNext = parent.getStartToken();
175 				target = targetNext.getPrev();
176 				parent.setStartToken(append);
177 			} else if (index == parent.getChildCount()) {
178 				target = parent.getStopToken();
179 				targetNext = target.getNext();
180 				parent.setStopToken(append);
181 			} else {
182 				LinkedListTree beforeChild = (LinkedListTree)parent.getChild(index);
183 				targetNext = beforeChild.getStartToken();
184 				target = targetNext.getPrev();
185 			}
186 			insertAfter(target, targetNext,
187 			            append, append);
188 		}
189 	}
190 
191 	public void deletedChild(LinkedListTree parent, int index, LinkedListTree child) {
192 		// FIXME: this should update start/stop tokens for the parent
193 		//        when the first/last child is removed
194 		LinkedListToken start = child.getStartToken();
195 		LinkedListToken stop = child.getStopToken();
196 		LinkedListToken startPrev = start.getPrev();
197 		LinkedListToken stopNext = stop.getNext();
198 //		if (startPrev == null) {
199 //			throw new IllegalArgumentException("No start.prev: "+child);
200 //		}
201 //		if (stopNext == null) {
202 //			throw new IllegalArgumentException("No stop.next: "+child+" (stop="+stop+")");
203 //		}
204 		if (parent.getChildCount() == 0
205 		 && start == parent.getStartToken()
206 		 && stop == parent.getStopToken())
207 		{
208 			// So, the child provided all the tokens that made up
209 			// the parent, and removing it will leave nothing!  In
210 			// this case, we insert a 'placeholder' token just so
211 			// there's something in the token stream for the parent
212 			// to reference, and the parent remains anchored to the
213 			// appropriate location within the source code
214 			LinkedListToken placeholder = TokenBuilder.newPlaceholder(parent);
215 			startPrev.setNext(placeholder);
216 			stopNext.setPrev(placeholder);
217 		} else {
218 			if (startPrev != null) {
219 				startPrev.setNext(stopNext);
220 			} else if (stopNext != null) {  // so try the other way around,
221 				stopNext.setPrev(startPrev);
222 			}
223 			if (parent.getStartToken() == start) {
224 				parent.setStartToken(stopNext);
225 			}
226 			if (parent.getStopToken() == stop) {
227 				parent.setStopToken(startPrev);
228 			}
229 		}
230 		// just to save possible confusion, break links out from the
231 		// removed token list too,
232 		start.setPrev(null);
233 		stop.setNext(null);
234 	}
235 
236 	public void replacedChild(LinkedListTree tree, int index, LinkedListTree child, LinkedListTree oldChild) {
237 		// defensive assertions to catch bugs,
238 		if (child.getStartToken() == null) {
239 			throw new IllegalArgumentException("No startToken: "+child);
240 		}
241 		if (child.getStopToken() == null) {
242 			throw new IllegalArgumentException("No stopToken: "+child);
243 		}
244 		// link the new child's tokens in place of the old,
245 		LinkedListToken oldBefore = findOldBeforeToken(tree, index, child, oldChild);
246 		LinkedListToken oldAfter = findOldAfterToken(tree, index, child, oldChild);
247 		if (oldBefore != null) {
248 			oldBefore.setNext(child.getStartToken());
249 		}
250 		if (oldAfter != null) {
251 			oldAfter.setPrev(child.getStopToken());
252 		}
253 		// just to save possible confusion, break links out from the
254 		// removed token list too,
255 		oldChild.getStartToken().setPrev(null);
256 		oldChild.getStopToken().setNext(null);
257 
258 		if (tree.getStartToken() == oldChild.getStartToken()) {
259 			tree.setStartToken(child.getStartToken());
260 		}
261 		if (tree.getStopToken() == oldChild.getStopToken()) {
262 			tree.setStopToken(child.getStopToken());
263 		}
264 	}
265 
266 	private LinkedListToken findOldBeforeToken(LinkedListTree tree, int index, LinkedListTree child, LinkedListTree oldChild) {
267 		LinkedListToken oldStart = oldChild.getStartToken();
268 		if (oldStart == null) {
269 			throw new IllegalStateException("<"+oldChild+">, child "+index+" of <"+tree+">, had no startToken");
270 		}
271 		return oldStart.getPrev();
272 	}
273 
274 	private LinkedListToken findOldAfterToken(LinkedListTree tree, int index, LinkedListTree child, LinkedListTree oldChild) {
275 		LinkedListToken oldStop = oldChild.getStopToken();
276 		if (oldStop == null) {
277 			throw new IllegalStateException("<"+oldChild+">, child "+index+" of <"+tree+">, had no stopToken");
278 		}
279 		return oldStop.getNext();
280 	}
281 }