
org.openhab.tools.analysis.checkstyle.api.AbstractStaticCheck Maven / Gradle / Ivy
The newest version!
/**
* Copyright (c) 2010-2025 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.tools.analysis.checkstyle.api;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.jsoup.Jsoup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.FileText;
import com.puppycrawl.tools.checkstyle.api.MessageDispatcher;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.MutableDataSet;
/**
* Provides common functionality for different static code analysis checks
*
* @author Svilen Valkanov - Initial contribution, add Exception to findLineNumber method
* @author Mihaela Memova - Simplify findLineNumber method
* @author Velin Yordanov - Used FileText instead of File to avoid unnecessary IO
*/
public abstract class AbstractStaticCheck extends AbstractFileSetCheck {
private final Logger logger = LoggerFactory.getLogger(AbstractStaticCheck.class);
/**
* Finds the first occurrence of a text in a list of text lines representing the file content and
* returns the line number, where the text was found
*
*
* @param fileContent represents the text content
* @param searchedText the text that we are looking for
* @param startLineNumber the line number from which the search starts exclusive, to start the
* search of the beginning of the text the startLineNumber should be 0
* @return the number of the line starting from 1, where the searched text occurred for the first
* time
* @throws NoResultException when no match was found
*/
protected int findLineNumber(FileText fileContent, String searchedText, int startLineNumber)
throws NoResultException {
for (int lineNumber = startLineNumber; lineNumber < fileContent.size(); lineNumber++) {
String line = fileContent.get(lineNumber);
if (line.contains(searchedText)) {
// The +1 is to compensate the 0-based list and the 1-based text file
return lineNumber + 1;
}
}
String message = MessageFormat.format(
"`{0}` was not found in the file {1} starting from line `{2}`."
+ " Check if it is split between multiple lines or it is missing",
searchedText, fileContent.getFile().getAbsolutePath(), startLineNumber);
throw new NoResultException(message);
}
/**
* Finds the first occurrence of a text in a list of text lines representing the file content and
* returns the line number, where the text was found
*
* @param fileText represents the content of a file
* @param searchedText the text that we are looking for
* @param startLineNumber the line number from which the search starts exclusive, to start the
* search of the beginning of the text the startLineNumber should be 0
* @param warningMessage message to be logged as warning in case no match is found
* @return the number of the line starting from 1, where the searched text occurred for the first
* time, or 0 if no matches are found
*/
protected int findLineNumberSafe(FileText fileText, String searchedText, int startLineNumber,
String warningMessage) {
try {
return findLineNumber(fileText, searchedText, startLineNumber);
} catch (NoResultException e) {
logger.warn("{} Fall back to 0.", warningMessage, e);
return 0;
}
}
/**
* Parses the content of the given file as an XML document.
*
* @param fileText Represents the text contents of a file
* @return DOM Document object
* @throws CheckstyleException if an error occurred while trying to parse the file
*/
protected Document parseDomDocumentFromFile(FileText fileText) throws CheckstyleException {
try {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document document = builder.parse(getInputStream(fileText));
return document;
} catch (ParserConfigurationException e) {
throw new CheckstyleException("Serious configuration error occurred while creating a DocumentBuilder.", e);
} catch (SAXException e) {
throw new CheckstyleException("Unable to read from file: " + fileText.getFile().getAbsolutePath(), e);
} catch (IOException e) {
throw new CheckstyleException("Unable to open file: " + fileText.getFile().getAbsolutePath(), e);
}
}
/**
* Reads a properties list from a file
*
* @param fileText Represents the text contents of a file
* @return Properties object containing all the read properties
* @throws CheckstyleException if an error occurred while trying to parse the file
*/
protected Properties readPropertiesFromFile(FileText fileText) throws CheckstyleException {
try {
Properties properties = new Properties();
properties.load(getInputStream(fileText));
return properties;
} catch (FileNotFoundException e) {
throw new CheckstyleException("File: " + fileText.getFile().getAbsolutePath() + " does not exist.", e);
} catch (IOException e) {
throw new CheckstyleException("Unable to read properties from: " + fileText.getFile().getAbsolutePath(), e);
}
}
/**
* Parses the content of a given file as a HTML file
*
* @param fileText Represents the text contents of a file
* @return HTML Document representation of the file
*/
protected org.jsoup.nodes.Document parseHTMLDocumentFromFile(FileText fileText) {
String fileContent = fileText.getFullText().toString();
return Jsoup.parse(fileContent);
}
/**
* Compiles an XPathExpression
*
* @param expression the XPath expression
* @return compiled XPath expression
* @throws CheckstyleException if an error occurred during the compilation
*/
protected XPathExpression compileXPathExpression(String expression) throws CheckstyleException {
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
try {
return xpath.compile(expression);
} catch (XPathExpressionException e) {
throw new CheckstyleException("Unable to compile the expression" + expression, e);
}
}
/**
* Checks whether a file is empty
*
* @param fileText Represents the text contents of a file
* @return true if the file is empty, otherwise false
*/
protected boolean isEmpty(FileText fileText) {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(getInputStream(fileText)))) {
if (bufferedReader.readLine() == null) {
return true;
}
} catch (IOException e) {
return false;
}
return false;
}
/**
* Adds an entry in the report using the {@link MessageDispatcher}.
* Can be used in the {@link #finishProcessing()} where the {@link #log(int, String, Object...)}
* methods can't be used as the entries logged by them won't be included in the report.
*
* @param filePath the absolute path to the file. Although a relative path can be used,
* it is not recommended as it will make filtering harder
* @param line the line that will be added in the report
* @param fileName the name the file
* @param message the message that will be logged
*/
protected void logMessage(String filePath, int line, String fileName, String message) {
MessageDispatcher dispatcher = getMessageDispatcher();
dispatcher.fireFileStarted(filePath);
log(line, message, fileName);
fireErrors(filePath);
dispatcher.fireFileFinished(filePath);
}
/**
* Parsed the content of a markdown file.
*
* @param fileText Represents the text contents of a file
* @param parsingOptions parsing options
* @return The markdown node
*/
protected Node parseMarkdown(FileText fileText, MutableDataSet parsingOptions) {
Parser parser = Parser.builder(parsingOptions).build();
return parser.parse(fileText.getFullText().toString());
}
private InputStream getInputStream(FileText fileText) {
return new ByteArrayInputStream(fileText.getFullText().toString().getBytes(fileText.getCharset()));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy