Java tutorial
/* * Copyright 2008 Google Inc. * * 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 com.google.template.soy.basetree; import com.google.common.collect.Lists; import java.util.List; /** * Mixin implementation of the parent-specific aspect of the ParentNode interface. * Requires the master to be a ParentNode. * * <p> Important: Do not use outside of Soy code (treat as superpackage-private). * * <p> The parameter N represents the interface or class that is the superclass of all possible * children for the master ParentNode. E.g. for a Soy parse tree node, N is usually SoyNode, but for * SoyFileSetNode N is SoyFileNode, for SoyFileNode N is TemplateNode, etc; for a Soy expression * parse tree, N is usually ExprNode. * */ public final class MixinParentNode<N extends Node> { /** Just spaces. */ protected static final String SPACES = " "; /** The master node that delegates to this instance. */ private final ParentNode<N> master; /** The children of the master node (accessed via this instance). */ private final List<N> children; /** Whether the master node needs an env frame when being interpreted, or null if unknown. */ private Boolean needsEnvFrameDuringInterp; /** * @param master The master node that delegates to this instance. */ public MixinParentNode(ParentNode<N> master) { this.master = master; needsEnvFrameDuringInterp = null; children = Lists.newArrayList(); } /** * Copy constructor. * @param orig The node to copy. * @param newMaster The master node for the copy. */ public MixinParentNode(MixinParentNode<N> orig, ParentNode<N> newMaster, CopyState copyState) { this.master = newMaster; this.needsEnvFrameDuringInterp = orig.needsEnvFrameDuringInterp; this.children = Lists.newArrayListWithCapacity(orig.children.size()); for (N origChild : orig.children) { @SuppressWarnings("unchecked") N newChild = (N) origChild.copy(copyState); this.children.add(newChild); newChild.setParent(this.master); } } /** * Gets the number of children. * @return The number of children. */ public int numChildren() { return children.size(); } /** * Gets the child at the given index. * @param index The index of the child to get. * @return The child at the given index. */ public N getChild(int index) { return children.get(index); } /** * Finds the index of the given child. * @param child The child to find the index of. * @return The index of the given child, or -1 if the given child is not a child of this node. */ public int getChildIndex(N child) { return children.indexOf(child); } /** * Gets the list of children. * * Note: The returned list is not a copy. Please do not modify the list directly. Instead, use * the other methods in this class that are intended for modifying children. Also, if you're * iterating over the children list as you're modifying it, then you should first make a copy of * the children list to iterate over, in order to avoid ConcurrentModificationException. * * @return The list of children. */ public List<N> getChildren() { return children; } /** * Adds the given child. * @param child The child to add. */ public void addChild(N child) { children.add(child); child.setParent(master); } /** * Adds the given child at the given index (shifting existing children if necessary). * @param index The index to add the child at. * @param child The child to add. */ public void addChild(int index, N child) { children.add(index, child); child.setParent(master); } /** * Removes the child at the given index. * @param index The index of the child to remove. */ public void removeChild(int index) { N child = children.remove(index); child.setParent(null); } /** * Removes the given child. * @param child The child to remove. */ public void removeChild(N child) { children.remove(child); child.setParent(null); } /** * Replaces the child at the given index with the given new child. * @param index The index of the child to replace. * @param newChild The new child. */ public void replaceChild(int index, N newChild) { N oldChild = children.set(index, newChild); oldChild.setParent(null); newChild.setParent(master); } /** * Replaces the given current child with the given new child. * @param currChild The current child to be replaced. * @param newChild The new child. */ public void replaceChild(N currChild, N newChild) { replaceChild(getChildIndex(currChild), newChild); } /** * Clears the list of children. */ public void clearChildren() { children.clear(); } /** * Adds the given children. * @param children The children to add. */ public void addChildren(List<? extends N> children) { for (N child : children) { addChild(child); } } /** * Adds the given children at the given index (shifting existing children if necessary). * @param index The index to add the children at. * @param children The children to add. */ public void addChildren(int index, List<? extends N> children) { List<N> origChildren = Lists.newArrayList(this.children); int origNumChildren = this.children.size(); // Temporarily remove the original children from index onward (in reverse order). for (int i = origNumChildren - 1; i >= index; i--) { removeChild(i); } // Add the new children. addChildren(children); // Add back the original children that we temporarily removed (in correct order). addChildren(origChildren.subList(index, origNumChildren)); } /** * Appends the source strings for all the children to the given StringBuilder. * @param sb The StringBuilder to which to append the children's source strings. */ public void appendSourceStringForChildren(StringBuilder sb) { for (N child : children) { sb.append(child.toSourceString()); } } /** * Appends the tree strings for all the children to the given StringBuilder, at one further * indentation level (3 spaces) than the given current indentation level. * @param sb The StringBuilder to which to append the children's tree strings. * @param indent The current indentation level of this parent node. */ public void appendTreeStringForChildren(StringBuilder sb, int indent) { for (N child : children) { sb.append(child.toTreeString(indent + 3)); } } /** * Builds a string that visually shows the subtree rooted at this node (for debugging). * Each line of the string will be indented by the given indentation amount. You should pass an * indentation of 0 unless this method is being called as part of building a larger tree string. * @param indent The indentation for each line of the tree string (usually pass 0). * @return A string that visually shows the subtree rooted at this node. */ public String toTreeString(int indent) { StringBuilder sb = new StringBuilder(); sb.append(SPACES, 0, indent).append("[").append(master).append("]\n"); appendTreeStringForChildren(sb, indent); return sb.toString(); } }