com.google.gxp.compiler.base..svn.text-base.Concatenation.svn-base Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of google-gxp Show documentation
Show all versions of google-gxp Show documentation
Google XML Pages (GXP) is a templating system used to generate XML/SGML markup (most often HTML).
The newest version!
/*
* Copyright (C) 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.gxp.compiler.base;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.gxp.compiler.alerts.SourcePosition;
import com.google.gxp.compiler.schema.Schema;
import java.util.*;
/**
* An {@code Expression} which concatenates other {@code Expression}s together.
*/
public class Concatenation extends Expression {
private final ImmutableList values;
private Concatenation(Node fromNode, Schema schema,
List values) {
super(fromNode, schema);
this.values = ImmutableList.copyOf(values);
if (values.size() < 2) {
throw new IllegalArgumentException(
"Concatenation cannot have only " + values.size() + " values.");
}
}
/**
* @param sourcePosition sourcePosition of the result, used only when
* concatentation turns out to be empty. (otherwise the values will be used
* to compute this)
* @param values values to concatenate.
* @return concatenated Expression. This may be a Concatenation, though it
* some cases constant folding will result in it being a different type of
* Expression.
*/
public static Expression create(SourcePosition sourcePosition,
Schema schema,
List values) {
// TODO(laurence): add more unit tests for this
// TODO(laurence): add some better consistency checks for concatenating
// nodes of inconsistent type. This is necessary for content flattener, as
// it has to deal with cases like "foo getValues() {
return values;
}
public Expression withValues(List newValues) {
return Iterables.elementsEqual(newValues, values)
? this
: create(getSourcePosition(), getSchema(), newValues);
}
@Override
public T acceptVisitor(ExpressionVisitor visitor) {
return visitor.visitConcatenation(this);
}
/**
* Simplifies a list of values that are to be concatenated by flattening out
* nested {@code Concatenation}s and merging adjacent {@code
* StringConstant}s.
*/
private static final List simplify(Schema schema,
List values) {
List result = Lists.newArrayList();
Node fromNode = null;
StringBuilder sb = new StringBuilder();
fromNode = simplifyHelper(schema, values, result, fromNode, sb);
if ((fromNode != null) && (sb.length() > 0)) {
result.add(new StringConstant(fromNode, schema,
sb.toString()));
}
return result;
}
/**
* Helper function for simplify.
* @param values the input values to be concatenated
* @param result the list to append the sub values of the concatenation to
* @param fromNode the Node from which the currently accumulated string was
* derived from, or null if there is no currently accumulated string
* @param sb the currently accumulated string
* @return the new value for fromNode
*/
private static final Node simplifyHelper(Schema schema,
List values,
List result,
Node fromNode,
StringBuilder sb) {
for (Expression value : values) {
value = Preconditions.checkNotNull(value);
if (value instanceof StringConstant) {
if (fromNode == null) {
fromNode = value;
}
StringConstant str = (StringConstant) value;
// TODO(laurence): this is another case where it's kind of weird that
// we just take one content type and start treating it like it's
// another. See "todo" above which discusses "reinterpret cast".
sb.append(str.evaluate());
} else if (value instanceof Concatenation) {
Concatenation subConcatenation = (Concatenation) value;
fromNode = simplifyHelper(schema, subConcatenation.values,
result, fromNode, sb);
} else {
if (fromNode != null) {
if (sb.length() > 0) {
result.add(new StringConstant(fromNode, schema, sb.toString()));
sb.setLength(0);
}
fromNode = null;
}
result.add(value);
}
}
return fromNode;
}
@Override
public boolean hasStaticString() {
for (Expression e : getValues()) {
if (!e.hasStaticString()) {
return false;
}
}
return true;
}
@Override
public boolean equals(Object that) {
return (that instanceof Concatenation) && equals((Concatenation) that);
}
public boolean equals(Concatenation that) {
return equalsExpression(that)
&& Iterables.elementsEqual(values, that.values);
}
@Override
public int hashCode() {
return Objects.hashCode(
expressionHashCode(),
values);
}
@Override
public List separate() {
return getValues();
}
}