com.google.common.css.compiler.passes.DisallowDuplicateDeclarations.java Source code

Java tutorial

Introduction

Here is the source code for com.google.common.css.compiler.passes.DisallowDuplicateDeclarations.java

Source

/*
 * Copyright 2011 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.passes;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import com.google.common.css.SourceCodeLocation;
import com.google.common.css.compiler.ast.CssCompilerPass;
import com.google.common.css.compiler.ast.CssDeclarationNode;
import com.google.common.css.compiler.ast.CssNode;
import com.google.common.css.compiler.ast.CssRulesetNode;
import com.google.common.css.compiler.ast.DefaultTreeVisitor;
import com.google.common.css.compiler.ast.ErrorManager;
import com.google.common.css.compiler.ast.GssError;
import com.google.common.css.compiler.ast.VisitController;

import java.util.Set;

/**
 * Compiler pass that reports an error if a ruleset has two of the same
 * declarations that are not marked as alternate. This must be run before
 * the SplitRulesetNodes pass.
 *
 * @author henrywong@google.com (Henry Wong)
 */
public class DisallowDuplicateDeclarations extends DefaultTreeVisitor implements CssCompilerPass {

    private static final String ERROR_STR = "Detected multiple identical, non-alternate declarations in the same "
            + "ruleset. If this is intentional please use the /* @alternate */ " + "annotation. ";

    private final VisitController visitController;
    private final ErrorManager errorManager;

    private final Set<String> propertyNames = Sets.newHashSet();

    public DisallowDuplicateDeclarations(VisitController visitController, ErrorManager errorManager) {
        this.visitController = visitController;
        this.errorManager = errorManager;
    }

    @Override
    public boolean enterRuleset(CssRulesetNode node) {
        for (CssNode child : node.getDeclarations().childIterable()) {
            // CssPropertyNodes don't get their location set, so just use the
            // location of the containing parent.
            processDeclaration((CssDeclarationNode) child, node.getSourceCodeLocation());
        }

        // Clear the map for future re-use
        propertyNames.clear();

        return true;
    }

    @Override
    public void runPass() {
        visitController.startVisit(this);
    }

    /**
     * Tests a given declaration to see whether or not it has a property name
     * that we've seen before.
     */
    @VisibleForTesting
    void processDeclaration(CssDeclarationNode declaration, SourceCodeLocation location) {

        String propertyName = declaration.getPropertyName().getPropertyName();

        // The MarkRemovableRulesetNodes pass special cases these so there's no
        // reason to check them here.
        // TODO(henrywong): Move PROPERTIES_NOT_TO_BE_CHECKED somewhere else.
        if (MarkRemovableRulesetNodes.PROPERTIES_NOT_TO_BE_CHECKED.contains(propertyName)) {
            return;
        }

        // If the declaration is star-hacked then we make the star be part of
        // the property name to ensure that we do not consider hacked
        // declarations as overridden by the non-hacked ones.
        // TODO(henrywong): This is copied from MarkRemovableRulesetNodes. We
        // should refactor this logic probably.
        if (declaration.hasStarHack()) {
            propertyName = "*" + propertyName;
        }

        // Ignore rules w/ the @alternate annotation.
        if (PassUtil.hasAlternateAnnotation(declaration)) {
            return;
        }

        if (propertyNames.contains(propertyName)) {
            errorManager.report(new GssError(ERROR_STR + declaration, location));
        } else {
            propertyNames.add(propertyName);
        }
    }
}