use crate::error::{Error, ErrorKind};
use crate::structures::FQName;
use crate::{FQNameBuf, JavaClass, JavaClassParser};
use petgraph::prelude::*;
use std::collections::{HashMap, HashSet, VecDeque};
#[derive(Debug)]
pub struct InheritanceGraph {
graph: DiGraph<FQNameBuf, InheritKind>,
mapping: HashMap<FQNameBuf, (JavaClass, NodeIndex)>,
root: FQNameBuf,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum InheritKind {
Extends,
Implements,
}
impl InheritanceGraph {
fn new(class: JavaClass) -> Self {
let fcq = class.this().to_owned();
let mut graph = DiGraph::new();
let index = graph.add_node(fcq.clone());
let map = HashMap::from([(fcq.clone(), (class, index))]);
Self {
graph,
mapping: map,
root: fcq,
}
}
fn add_class(&mut self, class: JavaClass) -> bool {
if self.mapping.contains_key(class.this()) {
return false;
}
let index = self.graph.add_node(class.this().to_owned());
self.mapping.insert(class.this().to_owned(), (class, index));
true
}
fn add_inheritance(&mut self, class: &FQName, inherits: &FQName, ty: InheritKind) -> bool {
let Some(&(_, class)) = self.mapping.get(class) else {
eprintln!("doesn't contain class {}", class);
return false;
};
let Some(&(_, inherits)) = self.mapping.get(inherits) else {
eprintln!("doesn't contain class {}", inherits);
return false;
};
if self.graph.contains_edge(class, inherits) {
eprintln!(
"already contains edge between {} -> {} ({:?})",
self.graph[class],
self.graph[inherits],
self.graph.find_edge(class, inherits).unwrap()
);
return false;
}
self.graph.add_edge(class, inherits, ty);
true
}
fn get_class(&self, node_index: NodeIndex) -> &JavaClass {
let name = &*self.graph[node_index];
let (class, _) = self
.mapping
.get(name)
.expect("index didn't correspond to known class");
class
}
pub fn inherits<F: AsRef<FQName>>(
&self,
fqn: F,
) -> Result<Vec<(&JavaClass, InheritKind)>, Error> {
let fq_name = fqn.as_ref();
if !self.mapping.contains_key(fq_name) {
return Err(Error::from(ErrorKind::NoClassFound(
fq_name.to_fqname_buf(),
)));
}
let mut outout = vec![];
let mut visited: HashSet<&FQName> = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(fq_name);
while let Some(ptr) = queue.pop_front() {
if !visited.contains(ptr) {
let (_, from_index) = self.mapping[ptr];
let inherits = self.graph.edges(from_index);
for edge in inherits {
let &inherit = edge.weight();
let to_class = self.get_class(edge.target());
if !visited.contains(to_class.this()) {
outout.push((to_class, inherit));
queue.push_back(to_class.this())
}
}
visited.insert(ptr);
}
}
Ok(outout)
}
}
pub fn inspect(class: &JavaClass, parser: &JavaClassParser) -> Result<InheritanceGraph, Error> {
let mut graph = InheritanceGraph::new(class.clone());
let mut stack = vec![];
stack.push(class.clone());
while let Some(class) = stack.pop() {
let super_class = match parser.find_super(&class) {
Ok(o) => {
Some(o)
}
Err(e) => {
if let ErrorKind::NoClassFound(_) = e.kind() {
None
} else {
return Err(e)
}
}
};
if let Some(super_class) = super_class {
let super_class_name = super_class.this().to_fqname_buf();
if graph.add_class(super_class.clone()) {
stack.push(super_class);
}
if !graph.add_inheritance(class.this(), &super_class_name, InheritKind::Extends) {
return Err(Error::new(ErrorKind::AddingInheritanceFailed(
class.this().to_fqname_buf(),
)));
}
}
let interfaces = parser.find_interfaces(&class)?;
for interface in interfaces {
let interface_name = interface.this().to_fqname_buf();
if graph.add_class(interface.clone()) {
stack.push(interface);
}
if !graph.add_inheritance(class.this(), &interface_name, InheritKind::Implements) {
return Err(Error::new(ErrorKind::AddingInheritanceFailed(
class.this().to_fqname_buf(),
)));
}
}
}
Ok(graph)
}