| /* |
| * 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.gwt.resources.css.ast; |
| |
| import com.google.gwt.core.ext.Generator; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| /** |
| * Maps a named property to a Value. |
| */ |
| public class CssProperty extends CssNode { |
| |
| /** |
| * Represents a sequence of no-arg method invocations. |
| */ |
| public static class DotPathValue extends Value { |
| private final String path; |
| private final String suffix; |
| |
| public DotPathValue(String path) { |
| assert path != null : "path"; |
| this.path = path; |
| this.suffix = null; |
| } |
| |
| public DotPathValue(String path, String suffix) { |
| assert path != null : "path"; |
| assert suffix != null : "suffix"; |
| this.path = path; |
| this.suffix = suffix; |
| } |
| |
| @Override |
| public String getExpression() { |
| StringBuilder toReturn = new StringBuilder(); |
| toReturn.append(path.replace(".", "().")); |
| toReturn.append("()"); |
| if (suffix != null) { |
| toReturn.append(" + \""); |
| toReturn.append(Generator.escape(suffix)); |
| toReturn.append("\""); |
| } |
| return toReturn.toString(); |
| } |
| |
| public List<String> getParts() { |
| return Arrays.asList(path.split("\\.")); |
| } |
| |
| public String getPath() { |
| return path; |
| } |
| |
| public String getSuffix() { |
| return suffix; |
| } |
| |
| @Override |
| public DotPathValue isDotPathValue() { |
| return this; |
| } |
| |
| @Override |
| public String toCss() { |
| return "value(\"" + path + "\"" |
| + (suffix == null ? "" : (", \"" + suffix + "\"")) + ")"; |
| } |
| } |
| |
| /** |
| * Represents a literal Java expression. |
| */ |
| public static class ExpressionValue extends Value { |
| private final String expression; |
| |
| public ExpressionValue(String expression) { |
| this.expression = expression; |
| } |
| |
| @Override |
| public String getExpression() { |
| return expression; |
| } |
| |
| @Override |
| public ExpressionValue isExpressionValue() { |
| return this; |
| } |
| |
| @Override |
| public String toCss() { |
| return "/* Java expression */"; |
| } |
| |
| /** |
| * For debugging only. |
| */ |
| @Override |
| public String toString() { |
| return expression; |
| } |
| } |
| |
| /** |
| * Represents an identifier in the CSS source. |
| */ |
| public static class IdentValue extends Value { |
| private final String ident; |
| |
| public IdentValue(String ident) { |
| this.ident = ident; |
| } |
| |
| @Override |
| public String getExpression() { |
| return '"' + Generator.escape(ident) + '"'; |
| } |
| |
| public String getIdent() { |
| return ident; |
| } |
| |
| @Override |
| public IdentValue isIdentValue() { |
| return this; |
| } |
| |
| @Override |
| public String toCss() { |
| return ident; |
| } |
| } |
| |
| /** |
| * Represents a space-separated list of Values. |
| */ |
| public static class ListValue extends Value { |
| private final List<Value> values; |
| |
| public ListValue(List<Value> values) { |
| this.values = Collections.unmodifiableList(new ArrayList<Value>(values)); |
| } |
| |
| public ListValue(Value... values) { |
| this(Arrays.asList(values)); |
| } |
| |
| @Override |
| public String getExpression() { |
| StringBuilder toReturn = new StringBuilder(); |
| boolean first = true; |
| for (Iterator<Value> i = values.iterator(); i.hasNext();) { |
| Value value = i.next(); |
| if (!first && value.isSpaceRequired()) { |
| toReturn.append("\" \" +"); |
| } |
| toReturn.append(value.getExpression()); |
| if (i.hasNext()) { |
| toReturn.append("+ "); |
| } |
| first = false; |
| } |
| return toReturn.toString(); |
| } |
| |
| public List<Value> getValues() { |
| return values; |
| } |
| |
| @Override |
| public ListValue isListValue() { |
| return this; |
| } |
| |
| @Override |
| public String toCss() { |
| StringBuilder sb = new StringBuilder(); |
| for (Value v : values) { |
| sb.append(" ").append(v.toCss()); |
| } |
| return sb.substring(1); |
| } |
| } |
| |
| /** |
| * Represents a numeric value, possibly with attached units. |
| */ |
| public static class NumberValue extends Value { |
| private final String css; |
| private final String expression; |
| private final String units; |
| private final float value; |
| |
| public NumberValue(float value) { |
| this(value, null); |
| } |
| |
| public NumberValue(float value, String units) { |
| this.value = value; |
| this.units = units; |
| |
| String s; |
| int i = (int) value; |
| if (i == value) { |
| s = String.valueOf(i); |
| } else { |
| s = String.valueOf(value); |
| } |
| |
| if (units != null && value != 0) { |
| css = s + units; |
| expression = '"' + s + Generator.escape(units) + '"'; |
| } else if (value == 0) { |
| css = "0"; |
| expression = "\"0\""; |
| } else { |
| css = s; |
| expression = '"' + s + '"'; |
| } |
| } |
| |
| @Override |
| public String getExpression() { |
| return expression; |
| } |
| |
| public String getUnits() { |
| return units; |
| } |
| |
| public float getValue() { |
| return value; |
| } |
| |
| @Override |
| public NumberValue isNumberValue() { |
| return this; |
| } |
| |
| @Override |
| public String toCss() { |
| return css; |
| } |
| } |
| |
| /** |
| * Represents one or more quoted string literals. |
| */ |
| public static class StringValue extends Value { |
| private static String escapeValue(String s, boolean inDoubleQuotes) { |
| StringBuilder b = new StringBuilder(); |
| for (char c : s.toCharArray()) { |
| if (Character.isISOControl(c)) { |
| b.append('\\').append(Integer.toHexString(c).toUpperCase()).append( |
| " "); |
| } else { |
| switch (c) { |
| case '\'': |
| // Special case a single quote in a pair of double quotes |
| if (inDoubleQuotes) { |
| b.append(c); |
| } else { |
| b.append("\\'"); |
| } |
| break; |
| |
| case '"': |
| // Special case a single quote in a pair of single quotes |
| if (inDoubleQuotes) { |
| b.append("\\\""); |
| } else { |
| b.append(c); |
| } |
| break; |
| |
| case '\\': |
| b.append("\\\\"); |
| break; |
| |
| default: |
| b.append(c); |
| } |
| } |
| } |
| |
| return b.toString(); |
| } |
| |
| private final String value; |
| |
| public StringValue(String value) { |
| this.value = value; |
| } |
| |
| @Override |
| public String getExpression() { |
| // The escaped CSS content has to be escaped to be a valid Java literal |
| return "\"" + Generator.escape(toCss()) + "\""; |
| } |
| |
| public String getValue() { |
| return value; |
| } |
| |
| @Override |
| public StringValue isStringValue() { |
| return this; |
| } |
| |
| /** |
| * Returns a escaped, quoted representation of the underlying value. |
| */ |
| @Override |
| public String toCss() { |
| return '"' + escapeValue(value, true) + '"'; |
| } |
| } |
| |
| /** |
| * Represents a token in the CSS source. |
| */ |
| public static class TokenValue extends IdentValue { |
| |
| public TokenValue(String token) { |
| super(token); |
| } |
| |
| @Override |
| public boolean isSpaceRequired() { |
| return false; |
| } |
| } |
| |
| /** |
| * An abstract encapsulation of property values in GWT CSS. |
| */ |
| public abstract static class Value { |
| /** |
| * Generate a Java expression whose execution results in the value. |
| */ |
| public abstract String getExpression(); |
| |
| public DotPathValue isDotPathValue() { |
| return null; |
| } |
| |
| public ExpressionValue isExpressionValue() { |
| return null; |
| } |
| |
| public IdentValue isIdentValue() { |
| return null; |
| } |
| |
| public ListValue isListValue() { |
| return null; |
| } |
| |
| public NumberValue isNumberValue() { |
| return null; |
| } |
| |
| public boolean isSpaceRequired() { |
| return true; |
| } |
| |
| public StringValue isStringValue() { |
| return null; |
| } |
| |
| /** |
| * Generate a CSS expression that represents the Value. |
| */ |
| public abstract String toCss(); |
| |
| /** |
| * For debugging only. |
| */ |
| @Override |
| public String toString() { |
| return toCss(); |
| } |
| } |
| |
| private boolean important; |
| |
| private String name; |
| private ListValue value; |
| |
| public CssProperty(String name, Value value, boolean important) { |
| assert name.length() > 0 : "name"; |
| this.name = name; |
| setValue(value); |
| this.important = important; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public ListValue getValues() { |
| return value; |
| } |
| |
| public boolean isImportant() { |
| return important; |
| } |
| |
| public void setImportant(boolean important) { |
| this.important = important; |
| } |
| |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| public void setValue(Value value) { |
| this.value = value.isListValue(); |
| if (this.value == null) { |
| this.value = new ListValue(value); |
| } |
| } |
| |
| public void traverse(CssVisitor visitor, Context context) { |
| visitor.visit(this, context); |
| visitor.endVisit(this, context); |
| } |
| } |