/** * Copyright 2011 The ForPlay Authors * * 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 forplay.core; import java.util.List; import java.util.ArrayList; /** * Provides implementations for per-platform concrete {@link GroupLayer}s. Because of single * inheritance (and lack of traits) we have to delegate this implementation rather than provide an * abstract base class. */ public class GroupLayerImpl<L extends AbstractLayer> { /** This group's children. */ public List<L> children = new ArrayList<L>(); public void add(GroupLayer self, L child) { // check whether the last child has the same depth as this child, in which case append this // child to our list; this is a fast path for when all children have the same depth int count = children.size(), index; if (count == 0 || children.get(count-1).depth() == child.depth()) { index = count; } else { // otherwise find the appropriate insertion point via binary search index = findInsertion(child.depth()); } // remove the child from any existing parent, preventing multiple parents if (child.parent() != null) { child.parent().remove(child); } children.add(index, child); child.setParent(self); child.onAdd(); } // TODO: remove this when GroupLayer.add(int,Layer) is removed public void add(GroupLayer self, int index, L child) { // remove the child from any existing parent, preventing multiple parents if (child.parent() != null) { child.parent().remove(child); } children.add(index, child); child.setParent(self); child.onAdd(); } public void remove(GroupLayer self, L child) { int index = findChild(child, child.depth()); if (index < 0) { throw new UnsupportedOperationException( "Could not remove Layer because it is not a child of the GroupLayer"); } remove(index); } // TODO: remove this when GroupLayer.remove(int) is removed public void remove(GroupLayer self, int index) { remove(index); } public void clear(GroupLayer self) { while (!children.isEmpty()) { remove(children.size() - 1); } } public void destroy(GroupLayer self) { AbstractLayer[] toDestroy = children.toArray(new AbstractLayer[children.size()]); // first remove all children efficiently self.clear(); // now that the children have been detached, destroy them for (AbstractLayer child : toDestroy) { child.destroy(); } } public void onAdd(GroupLayer self) { for (L child : children) { child.onAdd(); } } public void onRemove(GroupLayer self) { for (L child : children) { child.onRemove(); } } public void depthChanged(GroupLayer self, Layer layer, float oldDepth) { // structuring things such that Java's type system knew what was going on here would require // making AbstractLayer and ParentLayer more complex than is worth it @SuppressWarnings("unchecked") L child = (L)layer; // it would be great if we could move an element from one place in an ArrayList to another // (portably), but instead we have to remove and re-add children.remove(findChild(child, oldDepth)); children.add(findInsertion(child.depth()), child); } private void remove(int index) { L child = children.remove(index); child.onRemove(); child.setParent(null); } // uses depth to improve upon a full linear search private int findChild(L child, float depth) { // findInsertion will find us some element with the same depth as the to-be-removed child int startIdx = findInsertion(depth); // search down for our child for (int ii = startIdx-1; ii >= 0; ii--) { L c = children.get(ii); if (c == child) { return ii; } if (c.depth() != depth) { break; } } // search up for our child for (int ii = startIdx, ll = children.size(); ii < ll; ii++) { L c = children.get(ii); if (c == child) { return ii; } if (c.depth() != depth) { break; } } return -1; } // who says you never have to write binary search? private int findInsertion(float depth) { int low = 0, high = children.size()-1; while (low <= high) { int mid = (low + high) >>> 1; float midDepth = children.get(mid).depth(); if (depth > midDepth) { low = mid + 1; } else if (depth < midDepth) { high = mid - 1; } else { return mid; } } return low; } }