All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.sass.internal.parser.SassExpression Maven / Gradle / Ivy

There is a newer version: 0.9.13
Show newest version
/*
 * Copyright 2000-2014 Vaadin Ltd.
 * 
 * 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.vaadin.sass.internal.parser;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import com.vaadin.sass.internal.expression.ArithmeticExpressionEvaluator;
import com.vaadin.sass.internal.expression.BinaryOperator;
import com.vaadin.sass.internal.tree.Node;
import com.vaadin.sass.internal.tree.Node.BuildStringStrategy;
import com.vaadin.sass.internal.tree.VariableNode;

/**
 * SassExpressions are used for representing and evaluating arithmetic
 * expressions.
 * 
 * @author Vaadin
 * 
 */
public class SassExpression implements SassListItem, Serializable {

    private List items;
    private int line = 0;
    private int column = 0;

    /**
     * Constructs a SassExpression from a list of items. The list is not copied
     * but used directly.
     * 
     * @param items
     *            list of items (not copied but used directly)
     */
    private SassExpression(List items) {
        if (!items.isEmpty()) {
            line = items.get(0).getLineNumber();
            column = items.get(0).getColumnNumber();
        }

        this.items = items;
    }

    /**
     * Creates a new expression containing the elements of the parameter items
     * but with trailing whitespace items eliminated. If items contains only one
     * element excluding the trailing whitespace, returns the only contained
     * element. Otherwise returns a SassExpression.
     * 
     * @param items
     *            one or more SassListItems.
     * @return A SassExpression corresponding to items. If there is only one
     *         item after the removal of whitespace, returns that item instead
     *         of a SassExpression.
     */
    public static SassListItem createExpression(SassListItem... items) {
        return createExpression(Arrays.asList(items));
    }

    /**
     * Creates a new expression containing the elements of the parameter items
     * but with trailing whitespace items eliminated. If items contains only one
     * element excluding the trailing whitespace, returns the only contained
     * element. Otherwise returns a SassExpression.
     * 
     * @param items
     *            A list of SassListItems.
     * @return A SassExpression corresponding to items. If there is only one
     *         item after the removal of whitespace, returns that item instead
     *         of a SassExpression.
     */
    public static SassListItem createExpression(List items) {
        // filter out trailing whitespace
        int lastNonWhitespace = items.size() - 1;
        while (lastNonWhitespace > 0
                && isWhitespace(items.get(lastNonWhitespace))) {
            --lastNonWhitespace;
        }
        if (lastNonWhitespace < items.size() - 1) {
            // copy needed as subList() returns a non-serializable list
            items = new ArrayList(items.subList(0,
                    lastNonWhitespace + 1));
        }
        if (items.size() == 1) {
            return items.get(0);
        } else {
            return new SassExpression(items);
        }
    }

    @Override
    public int getLineNumber() {
        return line;
    }

    @Override
    public int getColumnNumber() {
        return column;
    }

    public boolean containsArithmeticalOperator() {
        for (SassListItem item : items) {
            if (item.containsArithmeticalOperator()) {
                return true;
            }
        }
        int previousIndex = getNextNonspaceIndex(items, 0);
        int currentIndex = getNextNonspaceIndex(items, previousIndex + 1);
        int nextIndex = getNextNonspaceIndex(items, currentIndex + 1);
        if (nextIndex >= items.size()) {
            return false;
        }
        while (nextIndex < items.size()) {
            SassListItem previous = items.get(previousIndex);
            SassListItem current = items.get(currentIndex);
            SassListItem next = items.get(nextIndex);
            previousIndex = currentIndex;
            currentIndex = nextIndex;
            nextIndex = getNextNonspaceIndex(items, nextIndex + 1);
            if (!(current instanceof LexicalUnitImpl)) {
                continue;
            }
            short currentType = ((LexicalUnitImpl) current)
                    .getLexicalUnitType();
            if (currentType == BinaryOperator.DIV.type) {
                /*
                 * '/' is treated as an arithmetical operator when one of its
                 * operands is Variable, or there is another binary operator.
                 * Otherwise, '/' is treated as a CSS operator. If interpolation
                 * occurs on either side of a symbol '/', '*', '+' or ´-', the
                 * symbol is not treated as an arithmetical operator.
                 */
                if ((isVariable(previous) || isVariable(next))
                        && !containsInterpolation(previous)
                        && !containsInterpolation(next)) {
                    return true;
                }
            } else if (isOperator(currentType)
                    && !containsInterpolation(previous)
                    && !containsInterpolation(next)) {
                return true;
            }
        }
        return false;
    }

    private boolean isOperator(short type) {
        for (BinaryOperator operator : BinaryOperator.values()) {
            if (type == operator.type) {
                return true;
            }
        }
        return false;
    }

    private boolean isVariable(SassListItem item) {
        return item instanceof LexicalUnitImpl
                && ((LexicalUnitImpl) item).getLexicalUnitType() == LexicalUnitImpl.SCSS_VARIABLE;
    }

    private boolean containsInterpolation(SassListItem item) {
        return item.printState().matches(".*#[{][$][^\\s]+[}].*");
    }

    /**
     * Returns the index of the next non-whitespace item in list, starting from
     * startIndex (inclusive). If there are no non-whitespace items in
     * list[startIndex...list.size() - 1], returns list.size().
     * 
     * @param list
     *            A list.
     * @param startIndex
     *            The first index included in the search.
     * @return The smallest index i such that i >= startIndex && list.get(i)
     *         does not represent whitespace. If no such index exists, returns
     *         list.size().
     */
    public static int getNextNonspaceIndex(List list,
            int startIndex) {
        for (int i = startIndex; i < list.size(); ++i) {
            if (!isWhitespace(list.get(i))) {
                return i;
            }
        }
        return list.size();
    }

    @Override
    public SassListItem evaluateFunctionsAndExpressions(
            boolean evaluateArithmetics) {
        List list = new ArrayList();
        for (SassListItem item : items) {
            list.add(item.evaluateFunctionsAndExpressions(evaluateArithmetics));
        }
        if (list.size() == 0 || !evaluateArithmetics) {
            return new SassExpression(list);
        } else {
            return ArithmeticExpressionEvaluator.get().evaluate(list);
        }
    }

    @Override
    public SassExpression replaceVariables(Collection variables) {
        List list = new ArrayList();
        for (SassListItem item : items) {
            list.add(item.replaceVariables(variables));
        }
        return new SassExpression(list);
    }

    @Override
    public void updateUrl(String prefix) {
        for (SassListItem item : items) {
            item.updateUrl(prefix);
        }
    }

    @Override
    public String printState() {
        return buildString(Node.PRINT_STRATEGY);
    }

    @Override
    public String buildString(BuildStringStrategy strategy) {
        String result = "";
        Iterator it = items.iterator();
        while (it.hasNext()) {
            SassListItem item = it.next();
            result += strategy.build(item);
        }
        return result;
    }

    @Override
    public String toString() {
        String result = "SassExpression[";
        result += buildString(Node.TO_STRING_STRATEGY);
        return result + "]";
    }

    @Override
    public String unquotedString() {
        if (items.size() == 1 && items.get(0) instanceof LexicalUnitImpl) {
            return ((LexicalUnitImpl) items.get(0)).unquotedString();
        }
        return printState();
    }

    @Override
    public LexicalUnitImpl getContainedValue() {
        if (items.size() != 1 || !(items.get(0) instanceof LexicalUnitImpl)) {
            throw new ParseException(
                    "getContainedValue() can only be used for an expression that contains one simple value. Actual value: "
                            + toString());
        }
        return (LexicalUnitImpl) items.get(0);
    }

    public static boolean isWhitespace(SassListItem unit) {
        return unit.printState().matches("\\s+");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy