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

org.sonarsource.analyzer.commons.EducationRuleLoader Maven / Gradle / Ivy

There is a newer version: 2.16.0.3141
Show newest version
/*
 * SonarSource Analyzers Commons
 * Copyright (C) 2009-2024 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonarsource.analyzer.commons;

import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.SonarRuntime;
import org.sonar.api.server.rule.Context;
import org.sonar.api.server.rule.RuleDescriptionSection;
import org.sonar.api.server.rule.RuleDescriptionSectionBuilder;
import org.sonar.api.server.rule.RulesDefinition.NewRule;
import org.sonar.api.utils.Version;

import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.HOW_TO_FIX_SECTION_KEY;
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.INTRODUCTION_SECTION_KEY;
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.RESOURCES_SECTION_KEY;
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ROOT_CAUSE_SECTION_KEY;

/**
 * This utility class helps loading the new content for progressive education rules.
 */
class EducationRuleLoader {

  private static final Logger LOG = LoggerFactory.getLogger(EducationRuleLoader.class);
  private static final String CODE_EXAMPLES_HEADER = "

Code examples

"; private static final String WHY_SECTION_HEADER = "

Why is this an issue\\?

"; private static final String HOW_TO_FIX_SECTION_HEADER = "

How to fix it

"; private static final String RESOURCES_SECTION_HEADER = "

Resources

"; private static final String HOW_TO_FIX_FRAMEWORK_SECTION_REGEX = "

How to fix it in (?:(?:an|a|the)\\s)?(?.*)

"; private static final Pattern HOW_TO_FIX_SECTION_PATTERN = Pattern.compile(HOW_TO_FIX_SECTION_HEADER); private static final Pattern HOW_TO_FIX_FRAMEWORK_SECTION_PATTERN = Pattern.compile(HOW_TO_FIX_FRAMEWORK_SECTION_REGEX); private static final Pattern WHY_SECTION_HEADER_PATTERN = Pattern.compile(WHY_SECTION_HEADER); // Runtime of the product in which the rules will be registered. This is needed as not all products/versions support the new format. private final SonarRuntime sonarRuntime; public EducationRuleLoader(SonarRuntime sonarRuntime) { this.sonarRuntime = sonarRuntime; } public void setEducationMetadataFromJson(NewRule rule, Map ruleMetadata) { if (!isEducationPrinciplesMetadataSupported()) { return; } Object propertyValue = ruleMetadata.get("educationPrinciples"); if (propertyValue instanceof List) { String[] educationPrincipleKeys = ((List) propertyValue).toArray(new String[0]); rule.addEducationPrincipleKeys(educationPrincipleKeys); } } public String setEducationDescriptionFromHtml(NewRule rule, String description) { if (!isEducationFormat(description)) { return description; } if (isEducationRuleDescriptionSupported()) { addEducationalRuleSections(rule, description); } return fallbackHtmlDescription(description); } /** * Creates a fallback HTML description based on the original education rule description. For products that do not support the new API yet, the descriptions are simplified to * only contain a single "How to fix it" section. */ static String fallbackHtmlDescription(String description) { Matcher m = HOW_TO_FIX_FRAMEWORK_SECTION_PATTERN.matcher(description); // We only need to do something if there are two or more "How to fix it" sections. if (m.find() && m.find()) { int indexOfResourceSection = description.indexOf(RESOURCES_SECTION_HEADER); String resourceSectionDescription = indexOfResourceSection > 0 ? description.substring(indexOfResourceSection) : ""; // The first part of the updated description is the beginning of the HTML file, up to the second "" found. return description.substring(0, m.start()) + resourceSectionDescription; } return description; } private static void addEducationalRuleSections(NewRule rule, String description) { // The "Why is this an issue?" section is expected. String[] split = description.split(WHY_SECTION_HEADER); // Adding the introduction section if not empty. addSection(rule, INTRODUCTION_SECTION_KEY, split[0]); split = split[1].split(RESOURCES_SECTION_HEADER); // Filtering out the "

Code examples

" title. String rootCauseAndHowToFixItSections = split[0].replace(CODE_EXAMPLES_HEADER, ""); // Either the generic "How to fix it" section or at least one framework specific "How to fix it in " section is expected. Matcher frameworkSpecificHowToFixItSectionMatcher = HOW_TO_FIX_FRAMEWORK_SECTION_PATTERN.matcher(rootCauseAndHowToFixItSections); boolean hasFrameworkSpecificHowToFixItSection = frameworkSpecificHowToFixItSectionMatcher.find(); boolean hasGenericHowToFixItSection = HOW_TO_FIX_SECTION_PATTERN.matcher(rootCauseAndHowToFixItSections).find(); if (hasGenericHowToFixItSection && hasFrameworkSpecificHowToFixItSection) { throw new IllegalStateException( String.format("Invalid education rule format for '%s', rule description has both generic and framework-specific 'How to fix it' sections", rule.key())); } else if (hasFrameworkSpecificHowToFixItSection) { // Splitting by the "How to fix in " will return an array where each element after the first is the content related to a given framework. String[] innerSplit = rootCauseAndHowToFixItSections.split(HOW_TO_FIX_FRAMEWORK_SECTION_REGEX); addSection(rule, ROOT_CAUSE_SECTION_KEY, innerSplit[0]); addContextSpecificHowToFixItSection(rule, innerSplit, frameworkSpecificHowToFixItSectionMatcher); } else if (hasGenericHowToFixItSection) { // Rule has the generic "How to fix it" section. String[] innerSplit = rootCauseAndHowToFixItSections.split(HOW_TO_FIX_SECTION_HEADER); addSection(rule, ROOT_CAUSE_SECTION_KEY, innerSplit[0]); addSection(rule, HOW_TO_FIX_SECTION_KEY, innerSplit[1]); } else { // No "How to fix it" section for the rule, the only section present is "Why is it an issue". addSection(rule, ROOT_CAUSE_SECTION_KEY, rootCauseAndHowToFixItSections); } // "Resources" section is optional. if (split.length > 1) { addSection(rule, RESOURCES_SECTION_KEY, split[1]); } } private static void addContextSpecificHowToFixItSection(NewRule rule, String[] split, Matcher m) { boolean match = true; int splitIndex = 1; while (match) { String displayName = m.group("displayName").trim(); String contextSpecificContent = split[splitIndex]; String key = displayName.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", "_"); addSection(rule, HOW_TO_FIX_SECTION_KEY, contextSpecificContent, new Context(key, displayName)); match = m.find(); splitIndex++; } } private static void addSection(NewRule rule, String sectionKey, String content) { addSection(rule, sectionKey, content, null); } private static void addSection(NewRule rule, String sectionKey, String content, @Nullable Context context) { String trimmedContent = content.trim(); if (trimmedContent.isEmpty()) { return; } RuleDescriptionSectionBuilder sectionBuilder = RuleDescriptionSection .builder() .sectionKey(sectionKey) .htmlContent(trimmedContent) .context(context); rule.addDescriptionSection(sectionBuilder.build()); } private static boolean isEducationFormat(String description) { return WHY_SECTION_HEADER_PATTERN.matcher(description).find(); } // Visible for testing boolean isEducationRuleDescriptionSupported() { return sonarRuntime.getApiVersion().isGreaterThanOrEqual(Version.create(9, 5)); } // Visible for testing boolean isEducationPrinciplesMetadataSupported() { return sonarRuntime.getApiVersion().isGreaterThanOrEqual(Version.create(9, 8)); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy