/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.flex.compiler.internal.tree.as; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.apache.flex.compiler.tree.as.IASNode; import org.apache.flex.utils.CheapArray; public abstract class TreeNode extends NodeBase { /** * Constructor. */ public TreeNode() { super(); children = CheapArray.create(getInitialChildCount()); } /** * Constructor. * * @param size The initial size of the array containing the children. */ public TreeNode(int size) { children = CheapArray.create(size); } private Object children; // // NodeBase overrides // @Override public int getChildCount() { return CheapArray.size(children); } @Override public IASNode getChild(int i) { return (IASNode)CheapArray.get(i, children); } @Override protected void replaceChild(NodeBase child, NodeBase target) { if (child != null) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { if (getChild(i) == child) { CheapArray.replace(i, target, children); target.setParent(this); child.setParent(null); return; } } } } @Override protected void swapChildren(NodeBase child, NodeBase target) { if (child != null && target != null && child.getParent() == target.getParent()) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { IASNode child2 = getChild(i); if (child2 == child) CheapArray.replace(i, target, children); else if (getChild(i) == target) CheapArray.replace(i, child, children); } } } @Override public void normalize(boolean fillInOffsets) { super.normalize(fillInOffsets); optimizeChildren(children); } // // Other methods // /** * Child nodes This collection is declared as Object, but is manipulated * exclusively using the functions in CheapArray. */ /** * Take a guess as to an efficient initial child count. We can override this * in nodes that have a fixed number of children. * * @return initial size to use when creating children */ protected int getInitialChildCount() { return 10; } /** * Add the node as a child. Used during parsing. * * @param child child node to add */ public void addChild(NodeBase child) { if (child != null) { CheapArray.add(child, children); child.setParent(this); } } public void addChild(NodeBase child, int position) { if (children instanceof List) { //TODO update offsets CheapArray.replace(position, child, emptyNodeArray); } else { ArrayList<IASNode> newArray = new ArrayList<IASNode>(); Collections.addAll(newArray, (IASNode[])children); newArray.add(position, child); children = newArray.toArray(); } child.setParent(this); } /** * Add the node as a child after normalization has occurred. Used after * parsing. * * @param child child node to add */ protected void addChildPostNormalize(NodeBase child) { if (child != null) { children = CheapArray.add(child, children, emptyNodeArray); child.setParent(this); } } /** * Add the node as a child, making sure it's in offset order with the other * children. This adds children from back to front, so it's most efficient * to add the children in order. Used during parsing. * * @param newChild child node to add * @param fillInOffsets true if we should fill in offsets as we go */ protected void addChildInOrder(NodeBase newChild, boolean fillInOffsets) { if (newChild != null) { // This child is new to the tree hierarchy, so it hasn't been normalized already newChild.normalize(fillInOffsets); int start = newChild.getAbsoluteStart() != -1 ? newChild.getAbsoluteStart() : newChild.getAbsoluteEnd(); if (start != -1) { int childrenSize = getChildCount(); if (childrenSize > 0) { if (start < (getChild(0)).getAbsoluteStart()) { CheapArray.add(0, newChild, children); newChild.setParent(this); return; } } for (int i = childrenSize - 1; i >= 0; i--) { IASNode sibling = getChild(i); if (sibling.getAbsoluteEnd() == -1 || sibling.getAbsoluteEnd() <= start) { // add child after sibling CheapArray.add(i + 1, newChild, children); newChild.setParent(this); return; } } } CheapArray.add(newChild, children); newChild.setParent(this); } } /** * Adds a node to the list of children WITHOUT changing their real parent. * * @param child new child node */ protected void addTemporaryChild(NodeBase child) { CheapArray.add(child, children); } /** * Remove the node as a child. Used during parsing. * * @param child child node to remove */ protected void removeChild(NodeBase child) { if (child != null) { //use safe remove call CheapArray.remove(child, children, emptyNodeArray); child.setParent(null); } } /** * Removes a node from the list of children WITHOUT changing their real * parent. * * @param child new child node */ protected void removeTemporaryChild(NodeBase child) { CheapArray.remove(child, children); } /** * Removes all of the children of this node */ @SuppressWarnings("rawtypes") public void removeAllChildren() { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { if (getChild(i) instanceof NodeBase) ((NodeBase)getChild(i)).setParent(null); } if (children instanceof ArrayList) children = new ArrayList(getInitialChildCount()); else children = emptyNodeArray; } /** * Sets our children to an optimized version of newChildren. * * @param newChildren */ protected void optimizeChildren(Object newChildren) { children = CheapArray.optimize(newChildren, emptyNodeArray); } protected void sortChildren(boolean updateBounds) { if (CheapArray.size(children) > 0) { CheapArray.sort(children, new Comparator<Object>() { @Override public int compare(Object o1, Object o2) { if (o1 instanceof NodeBase && o2 instanceof NodeBase) { if (((NodeBase)o1).getAbsoluteStart() < ((NodeBase)o2).getAbsoluteStart()) return -1; else if (((NodeBase)o1).getAbsoluteStart() == ((NodeBase)o2).getAbsoluteStart()) return 0; else return 1; } return -1; } }); if (updateBounds) { setStart(((NodeBase)CheapArray.get(0, children)).getAbsoluteStart()); setEnd(((NodeBase)CheapArray.get(CheapArray.size(children) - 1, children)).getAbsoluteEnd()); } } } }