/*
* 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.common.css.compiler.ast;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nullable;
/**
* Represents a list of nodes. This is meant to represent a succession of
* statements that logically group together as one node in the tree. Examples
* include:
* <ul>
* <li>all the statements in a stylesheet, except for the ones that can only
* appear on top ({@code charset}, {@code import}, etc.)
* <li>all the declarations inside a ruleset, a font face or a page rule
* <li>all the nodes that form a chain of if-elseif-else rules
* <li>the block that belongs to some of the at rules, such as {@code media}
* </ul>
*
* @param <T> the list is restricted to nodes of this type
*/
public abstract class CssNodesListNode<T extends CssNode> extends CssNode {
protected List<T> children = Lists.newArrayList();
private final boolean isEnclosedWithBraces;
/**
* Constructor of a list of nodes alike.
*
* @param isEnclosedWithBraces
*/
public CssNodesListNode(boolean isEnclosedWithBraces) {
this(isEnclosedWithBraces, null);
}
/**
* Constructor of a list of nodes alike.
*
* @param isEnclosedWithBraces
* @param comments
*/
public CssNodesListNode(boolean isEnclosedWithBraces,
@Nullable List<CssCommentNode> comments) {
super(null, comments, null);
this.isEnclosedWithBraces = isEnclosedWithBraces;
}
/**
* Constructor of a list of nodes alike.
*
* @param isEnclosedWithBraces
* @param comments
* @param childrenList list of children
*/
public CssNodesListNode(boolean isEnclosedWithBraces,
List<T> childrenList,
@Nullable List<CssCommentNode> comments) {
super(null, comments, null);
for (T child : childrenList) {
@SuppressWarnings("unchecked")
T childCopy = (T) child.deepCopy();
addChildToBack(childCopy);
}
this.isEnclosedWithBraces = isEnclosedWithBraces;
}
/**
* Copy constructor.
*
* @param node
*/
public CssNodesListNode(CssNodesListNode<? extends CssNode> node) {
super(
node.getParent(),
node.getComments(),
node.getSourceCodeLocation());
this.isEnclosedWithBraces = node.isEnclosedWithBraces;
for (CssNode child : node.childIterable()) {
@SuppressWarnings("unchecked")
T childCopy = (T) child.deepCopy();
addChildToBack(childCopy);
}
}
public List<T> getChildren() {
return Collections.unmodifiableList(children);
}
public Iterator<T> getChildIterator() {
return children.iterator();
}
public Iterable<T> childIterable() {
return Iterables.unmodifiableIterable(children);
}
void setChildren(List<T> children) {
Preconditions.checkArgument(!children.contains(null));
removeAsParentOfNodes(this.children);
this.children = copyToList(children);
becomeParentForNodes(this.children);
}
T removeChildAt(int index) {
Preconditions.checkState(index >= 0 && index < children.size());
T child = children.get(index);
removeAsParentOfNode(child);
children.remove(index);
return child;
}
// TODO(dgajda): Make it package private once we can walk the tree backwards
// and ReplaceConstantReferences won't need to use this method directly.
public void replaceChildAt(int index, List<? extends T> newChildren) {
Preconditions.checkState(index >= 0 && index < children.size());
Preconditions.checkArgument(!newChildren.contains(null));
removeChildAt(index);
children.addAll(index, newChildren);
becomeParentForNodes(newChildren);
}
public T getChildAt(int index) {
Preconditions.checkState(index >= 0 && index < children.size());
return children.get(index);
}
public int numChildren() {
return children.size();
}
T removeLastChild() {
return removeChildAt(children.size() - 1);
}
public T getLastChild() {
return children.get(children.size() - 1);
}
public void addChildToBack(T child) {
Preconditions.checkNotNull(child);
this.children.add(child);
becomeParentForNode(child);
}
public boolean isEmpty() {
return children.isEmpty();
}
public boolean isEnclosedWithBraces() {
return isEnclosedWithBraces;
}
/**
* For debugging only.
*/
@Override
public String toString() {
StringBuffer output = new StringBuffer();
if (!getComments().isEmpty()) {
output.append("[");
output.append(getComments().toString());
output.append(children.toString());
output.append("]");
} else {
output.append(children.toString());
}
return output.toString();
}
}