/* * BasicListUpdateDelegate.java * * Copyright (c) 2006 David Holroyd * * 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 uk.co.badgersinfoil.metaas.impl.antlr; import org.asdt.core.internal.antlr.AS3Parser; import uk.co.badgersinfoil.metaas.impl.ASTUtils; import uk.co.badgersinfoil.metaas.impl.TokenBuilder; /** * Manages the tokens of the parent tree in the most basic way possible, simply * inserting the run of tokens belonging to the child into the run of tokens * belonging to the parent and updating start/stop tokens for the parent if * required. */ public class BasicListUpdateDelegate implements TreeTokenListUpdateDelegate { // TODO: delete PLACEHOLDER tokens when they are superseded by the addition of real tokens public void addedChild(LinkedListTree parent, LinkedListTree child) { if (isPlaceholder(parent)) { if (isPlaceholder(child)) { 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"); } LinkedListToken placeholder = parent.getStartToken(); if (placeholder.getPrev() != null) { placeholder.getPrev().setNext(child.getStartToken()); } if (placeholder.getNext() != null) { placeholder.getNext().setPrev(child.getStopToken()); } parent.setStartToken(child.getStartToken()); parent.setStopToken(child.getStopToken()); return; } LinkedListToken stop = findTokenInsertionPointForChildWithinParent(parent, child); if (parent.getStartToken() == null) { parent.setStartToken(child.getStartToken()); } if (stop != null) { insertAfter(stop, stop.getNext(), child.getStartToken(), child.getStopToken()); } if (child.getStopToken() != null) { parent.setStopToken(child.getStopToken()); } } private boolean isPlaceholder(LinkedListTree ast) { return ast.getStartToken()==ast.getStopToken() && ast.getStartToken()!=null && ast.getStartToken().getType()==AS3Parser.VIRTUAL_PLACEHOLDER && ((PlaceholderLinkedListToken)ast.getStartToken()).getHeld()==ast; } private LinkedListToken findTokenInsertionPointForChildWithinParent(LinkedListTree parent, LinkedListTree child) { // FIXME: this fails to take into account am ancestor not // having the same kind of TreeTokenListUpdateDelegate while (parent != null) { if (parent.getChildCount() == 1) { // the just-added child is the only child of 'parent' if (parent.getStopToken() != null) { return parent.getStopToken(); } if (parent.getStartToken() != null) { return parent.getStartToken(); } } int index = parent.getIndexOfChild(child); if (index > 0 && index < parent.getChildCount()-1) { // 'child' is not the *first* child of 'parent' LinkedListTree precedent = (LinkedListTree)parent.getChild(index-1); if (precedent.getStopToken() == null) { // TODO: loop, rather than recurse, return findTokenInsertionPointForChildWithinParent(parent, precedent); } return precedent.getStopToken(); } if (index==0 && parent.getStartToken()!=null) { return parent.getStartToken(); } if (parent.getStopToken() != null) { return parent.getStopToken(); } child = parent; parent = parent.getParent(); } return null; } public void addedChild(LinkedListTree parent, int index, LinkedListTree child) { LinkedListToken target; LinkedListToken targetNext; if (index == 0) { LinkedListTree prevFirstChild = (LinkedListTree)parent.getChild(1); targetNext = prevFirstChild.getStartToken(); target = targetNext.getPrev(); if (targetNext == parent.getStartToken()) { parent.setStartToken(child.getStartToken()); } } else { target = ((LinkedListTree)parent.getChild(index - 1)).getStopToken(); targetNext = target.getNext(); } insertAfter(target, targetNext, child.getStartToken(), child.getStopToken()); } protected static void insertAfter(LinkedListToken target, LinkedListToken targetNext, LinkedListToken start, LinkedListToken stop) { if (target == null && targetNext == null) { throw new IllegalArgumentException("At least one of target and targetNext must be non-null"); } if (start != null) { // if (start.getPrev() != null || stop.getNext() != null) { // throw new IllegalArgumentException("insertAfter("+target+", "+targetNext+", "+start+", "+stop+") : start.getPrev()="+start.getPrev()+" stop.getNext()="+stop.getNext()); // } // i.e. we're not adding an imaginary node that currently // has no real children if (target != null) { target.setNext(start); } stop.setNext(targetNext); if (targetNext != null) { targetNext.setPrev(stop); } } } public void appendToken(LinkedListTree parent, LinkedListToken append) { if (parent.getStopToken() == null) { parent.setStartToken(append); parent.setStopToken(append); } else { // TODO: can this be simplified now? append.setNext(parent.getStopToken().getNext()); parent.getStopToken().setNext(append); append.setPrev(parent.getStopToken()); parent.setStopToken(append); } } public void addToken(LinkedListTree parent, int index, LinkedListToken append) { if (isPlaceholder(parent)) { LinkedListToken placeholder = parent.getStartToken(); parent.setStartToken(append); parent.setStopToken(append); placeholder.setPrev(null); placeholder.setNext(null); } if (parent.getStopToken() == null) { parent.setStartToken(append); parent.setStopToken(append); } else { LinkedListToken target; LinkedListToken targetNext; if (index == 0) { targetNext = parent.getStartToken(); target = targetNext.getPrev(); parent.setStartToken(append); } else if (index == parent.getChildCount()) { target = parent.getStopToken(); targetNext = target.getNext(); parent.setStopToken(append); } else { LinkedListTree beforeChild = (LinkedListTree)parent.getChild(index); targetNext = beforeChild.getStartToken(); target = targetNext.getPrev(); } insertAfter(target, targetNext, append, append); } } public void deletedChild(LinkedListTree parent, int index, LinkedListTree child) { // FIXME: this should update start/stop tokens for the parent // when the first/last child is removed LinkedListToken start = child.getStartToken(); LinkedListToken stop = child.getStopToken(); LinkedListToken startPrev = start.getPrev(); LinkedListToken stopNext = stop.getNext(); // if (startPrev == null) { // throw new IllegalArgumentException("No start.prev: "+child); // } // if (stopNext == null) { // throw new IllegalArgumentException("No stop.next: "+child+" (stop="+stop+")"); // } if (parent.getChildCount() == 0 && start == parent.getStartToken() && stop == parent.getStopToken()) { // So, the child provided all the tokens that made up // the parent, and removing it will leave nothing! In // this case, we insert a 'placeholder' token just so // there's something in the token stream for the parent // to reference, and the parent remains anchored to the // appropriate location within the source code LinkedListToken placeholder = TokenBuilder.newPlaceholder(parent); startPrev.setNext(placeholder); stopNext.setPrev(placeholder); } else { if (startPrev != null) { startPrev.setNext(stopNext); } else if (stopNext != null) { // so try the other way around, stopNext.setPrev(startPrev); } if (parent.getStartToken() == start) { parent.setStartToken(stopNext); } if (parent.getStopToken() == stop) { parent.setStopToken(startPrev); } } // just to save possible confusion, break links out from the // removed token list too, start.setPrev(null); stop.setNext(null); } public void replacedChild(LinkedListTree tree, int index, LinkedListTree child, LinkedListTree oldChild) { // defensive assertions to catch bugs, if (child.getStartToken() == null) { throw new IllegalArgumentException("No startToken: "+child); } if (child.getStopToken() == null) { throw new IllegalArgumentException("No stopToken: "+child); } // link the new child's tokens in place of the old, LinkedListToken oldBefore = findOldBeforeToken(tree, index, child, oldChild); LinkedListToken oldAfter = findOldAfterToken(tree, index, child, oldChild); if (oldBefore != null) { oldBefore.setNext(child.getStartToken()); } if (oldAfter != null) { oldAfter.setPrev(child.getStopToken()); } // just to save possible confusion, break links out from the // removed token list too, oldChild.getStartToken().setPrev(null); oldChild.getStopToken().setNext(null); if (tree.getStartToken() == oldChild.getStartToken()) { tree.setStartToken(child.getStartToken()); } if (tree.getStopToken() == oldChild.getStopToken()) { tree.setStopToken(child.getStopToken()); } } private LinkedListToken findOldBeforeToken(LinkedListTree tree, int index, LinkedListTree child, LinkedListTree oldChild) { LinkedListToken oldStart = oldChild.getStartToken(); if (oldStart == null) { throw new IllegalStateException("<"+oldChild+">, child "+index+" of <"+tree+">, had no startToken"); } return oldStart.getPrev(); } private LinkedListToken findOldAfterToken(LinkedListTree tree, int index, LinkedListTree child, LinkedListTree oldChild) { LinkedListToken oldStop = oldChild.getStopToken(); if (oldStop == null) { throw new IllegalStateException("<"+oldChild+">, child "+index+" of <"+tree+">, had no stopToken"); } return oldStop.getNext(); } }