org.sonar.plugins.web.checks.scripting.UnifiedExpressionCheck.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.plugins.web.checks.scripting.UnifiedExpressionCheck.java

Source

/*
 * Sonar Web Plugin
 * Copyright (C) 2010 Matthijs Galesloot
 * dev@sonar.codehaus.org
 *
 * 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 org.sonar.plugins.web.checks.scripting;

import java.lang.reflect.Method;

import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.FunctionMapper;
import javax.el.VariableMapper;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.jboss.el.lang.ExpressionBuilder;
import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.plugins.web.checks.AbstractPageCheck;
import org.sonar.plugins.web.node.Attribute;
import org.sonar.plugins.web.node.TagNode;

/**
 * Checker to validate Unified Expressions in JSF.
 *
 * @author Matthijs Galesloot
 * @since 1.0
 */
@Rule(key = "UnifiedExpressionCheck", name = "Invalid Expression", description = "Invalid expressions syntax", priority = Priority.BLOCKER)
public class UnifiedExpressionCheck extends AbstractPageCheck {

    /**
     * List of supported functions. Use of unknown functions raises a violation.
     * @since 1.1
     */
    @RuleProperty(key = "functions", description = "Functions (comma separated)")
    private String[] functions;

    public void setFunctions(String list) {
        functions = StringUtils.stripAll(StringUtils.split(list, ","));
    }

    private static final String[] JSTL_FUNCTIONS = new String[] { "contains", "containsIgnoreCase", "endsWith",
            "escapeXml", "indexOf", "join", "length", "replace", "split", "startsWith", "substring",
            "substringAfter", "substringBefore", "toLowerCase", "toUpperCase", "trim" };

    public String getFunctions() {
        if (functions != null) {
            return StringUtils.join(functions, ",");
        }
        return "";
    }

    /**
     * ELContext for use by ExpressionBuilder.
     */
    private class ExpressionLanguageContext extends ELContext {

        private final TagNode element;

        public ExpressionLanguageContext(TagNode element) {
            this.element = element;
        }

        @Override
        public ELResolver getELResolver() {
            return null;
        }

        @Override
        public FunctionMapper getFunctionMapper() {
            if (functions == null) {
                return null;
            } else {
                return new FunctionMapper() {

                    @Override
                    public Method resolveFunction(String prefix, String localName) {

                        if (!ArrayUtils.contains(JSTL_FUNCTIONS, localName)
                                && !ArrayUtils.contains(functions, localName)) {
                            createViolation(element.getStartLinePosition(), "Unknown function: " + localName);
                        }

                        return null; // we only care about the check.
                    }
                };
            }
        }

        @Override
        public VariableMapper getVariableMapper() {
            return null;
        }
    };

    @Override
    public void startElement(TagNode element) {

        for (Attribute attribute : element.getAttributes()) {
            String value = attribute.getValue();
            if (value != null) {
                value = value.trim();
                if (value.length() > 0 && isUnifiedExpression(value)) {
                    validateExpression(element, attribute);
                }
            }
        }
    }

    private void validateExpression(TagNode element, Attribute attribute) {
        ExpressionLanguageContext context = new ExpressionLanguageContext(element);
        ExpressionBuilder builder = new ExpressionBuilder(attribute.getValue(), context);

        try {
            builder.createValueExpression(Object.class);
        } catch (ELException e) {
            if (e.getMessage().startsWith("Error")) {
                createViolation(element.getStartLinePosition(), getRule().getDescription() + ": " + e.getMessage());
            }
        }
    }
}