com.google.common.css.compiler.ast.CssNode Maven / Gradle / Ivy
Show all versions of closure-stylesheets Show documentation
/*
* 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.Lists;
import com.google.common.collect.UnmodifiableIterator;
import com.google.common.css.SourceCodeLocation;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nullable;
/**
* A node of the abstract syntax tree.
*
* TODO(oana): Declare this class as:
* public abstract class CssNode {...}
*
*/
public abstract class CssNode {
/** The parent of this node. */
private CssNode parent;
/** The source code corresponding to this node. */
private SourceCodeLocation sourceCodeLocation;
/** List of comments/annotations. */
private List comments;
/** Annotation of a node to show whether it should be flipped. */
private boolean shouldBeFlipped = true;
/**
* Constructor of a node.
*/
public CssNode() {
this(null, null);
}
/**
* Constructor of a node.
*
* @param sourceCodeLocation
*/
public CssNode(@Nullable SourceCodeLocation sourceCodeLocation) {
this(null, sourceCodeLocation);
}
/**
* Constructor of a node.
*
* @param parent
*/
public CssNode(@Nullable CssNode parent) {
this(parent, null);
}
/**
* Constructor of a node.
*
* @param parent
* @param sourceCodeLocation
*/
public CssNode(@Nullable CssNode parent,
@Nullable SourceCodeLocation sourceCodeLocation) {
this(parent, null, sourceCodeLocation);
}
/**
* Constructor of a node.
*
* @param parent
* @param comments
* @param sourceCodeLocation
*/
public CssNode(@Nullable CssNode parent,
@Nullable List comments,
@Nullable SourceCodeLocation sourceCodeLocation) {
this.parent = parent;
this.sourceCodeLocation = sourceCodeLocation;
if (comments == null) {
this.comments = Lists.newArrayListWithExpectedSize(0);
} else {
this.comments = Lists.newArrayList(comments);
}
becomeParentForNodes(this.comments);
}
// TODO(oana): Declare this method as public abstract T deepCopy() to ensure
// that the return type is always correct.
public abstract CssNode deepCopy();
public CssNode getParent() {
return parent;
}
void setParent(CssNode parent) {
this.parent = parent;
}
public SourceCodeLocation getSourceCodeLocation() {
return sourceCodeLocation;
}
public void setSourceCodeLocation(
@Nullable SourceCodeLocation sourceCodeLocation) {
this.sourceCodeLocation = sourceCodeLocation;
}
/**
* Removes the relation between this node and its parent.
*/
void removeParent() {
this.parent = null;
}
/**
* Removes the parent-child relation between this node and a child.
*
* @param child
*/
void removeAsParentOfNode(CssNode child) {
if (child == null) {
return;
}
Preconditions.checkArgument(child.getParent() == this);
child.removeParent();
}
/**
* Removes the parent-children relations between this node and a list of
* children.
*
* @param children
*/
void removeAsParentOfNodes(List extends CssNode> children) {
if (children == null) {
return;
}
for (CssNode child : children) {
removeAsParentOfNode(child);
}
}
/**
* Makes this node the parent of a child.
*
* @param child
*/
final void becomeParentForNode(@Nullable CssNode child) {
if (child == null) {
return;
}
child.setParent(this);
}
/**
* Makes this node the parent of a list of children.
*
* @param children
*/
final void becomeParentForNodes(List extends CssNode> children) {
Preconditions.checkNotNull(children);
if (children.size() == 0) {
// otherwise we'll spend a lot of CPU collecting garbage due to
// the empty iterators we use below (as of June 2012, javac
// does not optimize the Collection case of enh-for).
return;
}
for (CssNode child : children) {
becomeParentForNode(child);
}
}
public void appendComment(CssCommentNode comment) {
comments.add(comment);
becomeParentForNode(comment);
}
public void setComments(List comments) {
Preconditions.checkNotNull(this.comments);
removeAsParentOfNodes(this.comments);
this.comments = Lists.newArrayList(comments);
becomeParentForNodes(this.comments);
}
public List getComments() {
return comments;
}
/**
* Returns whether one of the comments attached to this node exactly matches
* the given string.
*/
public boolean hasComment(String comment) {
for (CssCommentNode c : comments) {
if (c.getValue().equals(comment)) {
return true;
}
}
return false;
}
public boolean getShouldBeFlipped() {
return shouldBeFlipped;
}
/**
* Marks a node whether it should be flipped or not.
*
* @param shouldBeFlipped Whether the node should be flipped or not.
*/
public void setShouldBeFlipped(boolean shouldBeFlipped) {
this.shouldBeFlipped = shouldBeFlipped;
}
List copyToList(List list) {
return Lists.newArrayList(list);
}
/**
* This is the default implementation of {@code equals()}. The method is
* marked {@code final} so that nobody changes the default behavior.
*
* The rationale for not allowing redefinition of equals is that different
* compiler passes and different optimizations have their own notion of
* equality. In order to avoid confusion and errors everybody that needs a
* custom definition of equality and custom hash codes should use customized
* collections with custom comparators or hash code providers.
*
*
In addition, this class should not implement {@link Comparable}. Use
* {@link java.util.Comparator} instead.
*
* @see Object#equals(Object)
*/
@Override
public final boolean equals(@Nullable Object obj) {
return super.equals(obj);
}
/**
* This is the default implementation of {@code hashCode()}. The method is
* marked {@code final} so that nobody changes the default behavior.
*
* @see #equals(Object)
* @see Object#hashCode()
*/
@Override
public final int hashCode() {
return super.hashCode();
}
/**
* This is the default implementation of {@code toString()}.
*
*
Overriding this method should only be done for debugging or logging
* purposes, not for the actual functionality of the compiler. If a string
* representation of a tree is needed, define a Visitor that builds the
* desired representation.
*
* @see Object#toString()
*/
@Override
public String toString() {
return super.toString();
}
/**
* This node and the transitive closure of its {@link #parent}s.
*/
public Iterable ancestors() {
return new Iterable() {
public Iterator iterator() {
return new UnmodifiableIterator() {
private CssNode current = CssNode.this;
@Override
public boolean hasNext() {
return current != null;
}
@Override
public CssNode next() {
CssNode result = current;
current = current.getParent();
return result;
}
};
}
};
}
/**
* Returns true when any of a {@code node}'s {@link #ancestors} is a function
* node.
*/
public boolean inFunArgs() {
for (CssNode n : ancestors()) {
if (n instanceof CssFunctionNode) {
return true;
}
}
return false;
}
public VisitController getVisitController() {
return new DefaultVisitController(this, false /* allowMutating */);
}
}