/*
*
* 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());
}
}
}
}