Package com.bazaarvoice.jless.ast.node

Source Code of com.bazaarvoice.jless.ast.node.ScopeNode

/**
* Copyright 2010 Bazaarvoice, 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.
*
* @author J. Ryan Stinnett (ryan.stinnett@bazaarvoice.com)
*/

package com.bazaarvoice.jless.ast.node;

import com.bazaarvoice.jless.ast.util.NodeTreeUtils;
import com.bazaarvoice.jless.ast.visitor.InclusiveNodeVisitor;
import com.bazaarvoice.jless.ast.visitor.NodeAdditionVisitor;
import com.bazaarvoice.jless.ast.visitor.NodeNavigationVisitor;
import com.bazaarvoice.jless.exception.IllegalMixinArgumentException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ScopeNode extends InternalNode {

    private static final String FILTER_PROPERTY = "filter";

    private Map<String, ExpressionGroupNode> _variableNameToValueMap = new HashMap<String, ExpressionGroupNode>();
    private Map<String, RuleSetNode> _selectorToRuleSetMap = new HashMap<String, RuleSetNode>();
    private List<VariableDefinitionNode> _parameterDefinitions = new ArrayList<VariableDefinitionNode>();
    private Map<String, PropertyNode> _propertyNameToNodeMap = new HashMap<String, PropertyNode>();
    private ScopeNode _parentScope;
    private boolean _bracketsDisplayed = true;

    public ScopeNode() {
        super();
        setAdditionVisitor();
    }

    public ScopeNode(Node node) {
        this();
        addChild(node);
    }

    public ScopeNode(Collection<Node> nodeCollection) {
        this();
        addChildren(nodeCollection);
    }

    public boolean isVariableDefined(String name) {
        return _variableNameToValueMap.containsKey(name);
    }

    public ExpressionGroupNode getVariable(String name) {
        return _variableNameToValueMap.get(name);
    }

    public RuleSetNode getRuleSet(String selectorGroup) {
        return _selectorToRuleSetMap.get(selectorGroup);
    }

    public ScopeNode getParentScope() {
        return _parentScope;
    }

    public void setParentScope(ScopeNode parentScope) {
        _parentScope = parentScope;
    }

    public boolean isBracketsDisplayed() {
        return _bracketsDisplayed;
    }

    public void setBracketsDisplayed(boolean bracketsDisplayed) {
        _bracketsDisplayed = bracketsDisplayed;
    }

    /**
     * Creates a clone of this scope to be attached to the tree at the site of a mixin reference. If an ArgumentsNode is passed,
     * each of its values override those defined by the mixin's parameters.
     */
    public ScopeNode callMixin(String name, ArgumentsNode arguments) {
        List<ExpressionGroupNode> argumentList = (arguments != null) ? NodeTreeUtils.getChildren(arguments, ExpressionGroupNode.class) : Collections.<ExpressionGroupNode>emptyList();
        if (argumentList.size() > _parameterDefinitions.size()) {
            throw new IllegalMixinArgumentException(name, _parameterDefinitions.size());
        }

        // Clone scope and filter out any white space
        ScopeNode mixinScope = clone();
        NodeTreeUtils.filterLineBreaks(mixinScope);

        // If arguments were passed, apply them
        for (int i = 0; i < argumentList.size(); i++) {
            ExpressionGroupNode argument = argumentList.get(i);
            // Replace the value of the definition
            VariableDefinitionNode parameter = mixinScope._parameterDefinitions.get(i);
            parameter.clearChildren();
            parameter.addChild(argument);
        }

        // Mark this scope's containing rule set as invisible since it has been used as a mixin
        getParent().setVisible(false);

        return mixinScope;
    }

    /**
     * Some nodes are captured in additional structures to aid later resolution.
     */
    private void setAdditionVisitor() {
        setAdditionVisitor(new InclusiveNodeVisitor() {
            /**
             * Add parameter set as a child for printing input, but also add each defined value to the variable map.
             */
            @Override
            public boolean add(ParametersNode node) {
                for (VariableDefinitionNode variable : NodeTreeUtils.getChildren(node, VariableDefinitionNode.class)) {
                    _parameterDefinitions.add(variable);
                    add(variable);
                }
                return super.add(node);
            }

            /**
             * Store the rule set's scope by selector group
             */
            @Override
            public boolean add(RuleSetNode node) {
                SelectorGroupNode selectorGroup = NodeTreeUtils.getFirstChild(node, SelectorGroupNode.class);
                for (SelectorNode selectorNode : NodeTreeUtils.getChildren(selectorGroup, SelectorNode.class)) {
                    StringBuilder sb = new StringBuilder();
                    for (Node selectorChild : selectorNode.getChildren()) {
                        sb.append(selectorChild.toString());
                    }
                    String selector = sb.toString();
                    // Mixins lock on first definition
                    if (!_selectorToRuleSetMap.containsKey(selector)) {
                        _selectorToRuleSetMap.put(selector, node);
                    }
                }
                return super.add(node);
            }

            /**
             * Absorb all children of the given scope. This assumes that cloning is not necessary.
             */
            @Override
            public boolean add(ScopeNode node) {
                NodeTreeUtils.moveChildren(node, ScopeNode.this);
                return false; // Don't add the original scope itself
            }

            /**
             * Store variable definitions in a map by name
             */
            @Override
            public boolean add(VariableDefinitionNode node) {
                String name = node.getName();
                // "Variables" lock on first definition
                if (!_variableNameToValueMap.containsKey(name)) {
                    _variableNameToValueMap.put(name, NodeTreeUtils.getFirstChild(node, ExpressionGroupNode.class));
                }
                return super.add(node);
            }

            /**
             * Store property nodes by name. If there are multiple properties for a given name, only retain the last one.
             */
            @Override
            public boolean add(PropertyNode node) {
                String name = node.getName();

                // If this is the IE-specific "filter" property, always add it
                if (name.equals(FILTER_PROPERTY)) {
                    return super.add(node);
                }

                // If the value of this property node is a vendor-specific keyword, always add it
                if (node.getChildren().get(0).toString().startsWith("-")) {
                    return super.add(node);
                }

                // Check if this property has been seen before
                if (_propertyNameToNodeMap.containsKey(name)) {
                    PropertyNode oldPropertyNode = _propertyNameToNodeMap.get(name);
                    int oldPropertyIndex = getChildren().indexOf(oldPropertyNode);

                    if (oldPropertyNode.isVisible()) {
                        // Hide the unneeded property
                        oldPropertyNode.setVisible(false);

                        // Attempt to hide one surrounding white space node
                        if (!hideWhiteSpaceNode(oldPropertyIndex - 1)) {
                            hideWhiteSpaceNode(oldPropertyIndex + 1);
                        }
                    }
                }

                // Store the property as the latest for this name
                _propertyNameToNodeMap.put(name, node);

                return super.add(node);
            }
        });
    }

    private boolean hideWhiteSpaceNode(int index) {
        if (index < 0 || index >= getChildren().size()) {
            return false;
        }

        Node node = getChildren().get(index);
        if (!(node instanceof WhiteSpaceCollectionNode || node instanceof SpacingNode) || !node.isVisible()) {
            return false;
        }

        node.setVisible(false);
        return true;
    }

    @Override
    protected boolean add(NodeAdditionVisitor visitor) {
        return visitor.add(this);
    }

    @Override
    protected boolean enter(NodeNavigationVisitor visitor) {
        return visitor.enter(this);
    }

    @Override
    protected boolean exit(NodeNavigationVisitor visitor) {
        return visitor.exit(this);
    }

    @Override
    protected boolean visitInvisible(NodeNavigationVisitor visitor) {
        return visitor.visitInvisible(this);
    }

    /**
     * The internal state of this node will be rebuilt as cloned children are added by the super class.
     */
    @Override
    public ScopeNode clone() {
        return (ScopeNode) super.clone();
    }

    /**
     * Recreate internal state before children are cloned.
     */
    @Override
    protected void cloneChildren(InternalNode node) {
        ScopeNode scope = (ScopeNode) node;

        // Reset internal state
        scope._variableNameToValueMap = new HashMap<String, ExpressionGroupNode>();
        scope._selectorToRuleSetMap = new HashMap<String, RuleSetNode>();
        scope._parameterDefinitions = new ArrayList<VariableDefinitionNode>();
        scope._propertyNameToNodeMap = new HashMap<String, PropertyNode>();
        scope.setAdditionVisitor();

        super.cloneChildren(node);
    }
}
TOP

Related Classes of com.bazaarvoice.jless.ast.node.ScopeNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.