use crate::checkstyle::api::ast::DetailAst;
use crate::checkstyle::api::check::Check;
use crate::checkstyle::api::config::{Configurable, Context, Contextualizable};
use crate::checkstyle::api::error::CheckstyleResult;
use crate::checkstyle::checks::base::AbstractCheck;
use crate::checkstyle::utils::full_ident::FullIdent;
use std::collections::HashSet;
pub struct RedundantImportCheck {
base: AbstractCheck,
imports: HashSet<String>,
static_imports: HashSet<String>,
package_name: Option<String>,
}
impl RedundantImportCheck {
pub fn new() -> Self {
Self {
base: AbstractCheck::new("RedundantImport".to_string()),
imports: HashSet::new(),
static_imports: HashSet::new(),
package_name: None,
}
}
fn is_from_package(import_name: &str, pkg: &str) -> bool {
if let Some(index) = import_name.rfind('.') {
let front = &import_name[..index];
pkg == front
} else {
false
}
}
}
impl Default for RedundantImportCheck {
fn default() -> Self {
Self::new()
}
}
impl Configurable for RedundantImportCheck {
fn configure(&mut self, config: &crate::checkstyle::api::config::Configuration) -> CheckstyleResult<()> {
self.base.configure(config)
}
}
impl Contextualizable for RedundantImportCheck {
fn contextualize(&mut self, context: &Context) -> CheckstyleResult<()> {
self.base.contextualize(context)
}
}
impl Check for RedundantImportCheck {
fn get_default_tokens(&self) -> Vec<i32> {
self.get_required_tokens()
}
fn get_acceptable_tokens(&self) -> Vec<i32> {
self.get_required_tokens()
}
fn get_required_tokens(&self) -> Vec<i32> {
vec![
crate::checkstyle::api::ast::token_types::IMPORT,
crate::checkstyle::api::ast::token_types::STATIC_IMPORT,
crate::checkstyle::api::ast::token_types::PACKAGE_DEF,
]
}
fn begin_tree(&mut self, _ast: &dyn DetailAst) -> CheckstyleResult<()> {
self.package_name = None;
self.imports.clear();
self.static_imports.clear();
Ok(())
}
fn visit_token(&mut self, ast: &dyn DetailAst) -> CheckstyleResult<()> {
let token_type = ast.get_type();
if token_type == crate::checkstyle::api::ast::token_types::PACKAGE_DEF {
if let Some(name_node) = ast.get_first_child_arc() {
let mut current = Some(name_node);
while let Some(node) = current {
let node_type = node.get_type();
if node_type == crate::checkstyle::api::ast::token_types::DOT
|| node_type == crate::checkstyle::api::ast::token_types::IDENT
{
let full_ident = FullIdent::create_full_ident(&node);
self.package_name = Some(full_ident.get_text().to_string());
break;
} else if node_type == crate::checkstyle::api::ast::token_types::MODIFIERS {
current = node.get_next_sibling_arc();
} else {
current = node.get_first_child_arc();
}
}
}
} else if token_type == crate::checkstyle::api::ast::token_types::IMPORT {
if let Some(import_node) = ast.get_first_child_arc() {
let full_ident = FullIdent::create_full_ident_below(&import_node);
let import_text = full_ident.get_text();
if Self::is_from_package(import_text, "java.lang") {
self.base.log_ast(
ast,
"import.lang".to_string(),
vec![import_text.to_string()],
);
}
else if let Some(ref pkg) = self.package_name {
if Self::is_from_package(import_text, pkg) {
self.base.log_ast(
ast,
"import.same".to_string(),
vec![import_text.to_string()],
);
}
}
if self.imports.contains(import_text) {
self.base.log_ast(
ast,
"import.duplicate".to_string(),
vec![import_text.to_string()],
);
} else {
self.imports.insert(import_text.to_string());
}
}
} else if token_type == crate::checkstyle::api::ast::token_types::STATIC_IMPORT {
if let Some(import_node) = ast.get_first_child_arc() {
let full_ident = FullIdent::create_full_ident(&import_node);
let import_text = full_ident.get_text();
if self.static_imports.contains(import_text) {
self.base.log_ast(
ast,
"import.duplicate".to_string(),
vec![import_text.to_string()],
);
} else {
self.static_imports.insert(import_text.to_string());
}
}
}
Ok(())
}
fn get_violations(&self) -> std::collections::BTreeSet<crate::checkstyle::api::violation::Violation> {
self.base.get_violations()
}
fn clear_violations(&mut self) {
self.base.clear_violations();
}
}