|
|
|
@ -19,20 +19,46 @@ class CodeAnalyzer(ABC):
|
|
|
|
|
class JavaAnalyzer(CodeAnalyzer):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.classes = []
|
|
|
|
|
self.class_names = set()
|
|
|
|
|
self.relationships = []
|
|
|
|
|
|
|
|
|
|
def analyze_directory(self, directory_path):
|
|
|
|
|
|
|
|
|
|
self._collect_class_names(directory_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return self._analyze_classes_and_generate_uml(directory_path)
|
|
|
|
|
|
|
|
|
|
def _collect_class_names(self, directory_path):
|
|
|
|
|
|
|
|
|
|
for root, _, files in os.walk(directory_path):
|
|
|
|
|
for file in files:
|
|
|
|
|
if file.endswith('.java'):
|
|
|
|
|
try:
|
|
|
|
|
with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
|
|
|
|
|
content = f.read()
|
|
|
|
|
|
|
|
|
|
tree = javalang.parse.parse(content)
|
|
|
|
|
for path, node in tree.filter(javalang.tree.ClassDeclaration):
|
|
|
|
|
self.class_names.add(node.name)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Erreur lors de la collecte des classes dans {file}: {str(e)}")
|
|
|
|
|
|
|
|
|
|
def _analyze_classes_and_generate_uml(self, directory_path):
|
|
|
|
|
|
|
|
|
|
for root, _, files in os.walk(directory_path):
|
|
|
|
|
for file in files:
|
|
|
|
|
if file.endswith('.java'):
|
|
|
|
|
self._analyze_file(os.path.join(root, file))
|
|
|
|
|
return self.classes, self.relationships
|
|
|
|
|
|
|
|
|
|
return self.classes, self.get_relationships()
|
|
|
|
|
|
|
|
|
|
def _analyze_file(self, file_path):
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with open(file_path, 'r', encoding='utf-8') as file:
|
|
|
|
|
content = file.read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tree = javalang.parse.parse(content)
|
|
|
|
|
for path, node in tree.filter(javalang.tree.ClassDeclaration):
|
|
|
|
|
class_info = {
|
|
|
|
@ -40,10 +66,14 @@ class JavaAnalyzer(CodeAnalyzer):
|
|
|
|
|
'methods': [],
|
|
|
|
|
'attributes': [],
|
|
|
|
|
'extends': node.extends.name if node.extends else None,
|
|
|
|
|
'implements': [impl.name for impl in node.implements] if node.implements else []
|
|
|
|
|
'implements': [impl.name for impl in node.implements] if node.implements else [],
|
|
|
|
|
'associations': [],
|
|
|
|
|
'dependencies': [],
|
|
|
|
|
'aggregations': [],
|
|
|
|
|
'compositions': []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Analyse des méthodes
|
|
|
|
|
for method in node.methods:
|
|
|
|
|
method_info = {
|
|
|
|
|
'name': method.name,
|
|
|
|
@ -51,8 +81,14 @@ class JavaAnalyzer(CodeAnalyzer):
|
|
|
|
|
'parameters': [(param.type.name, param.name) for param in method.parameters]
|
|
|
|
|
}
|
|
|
|
|
class_info['methods'].append(method_info)
|
|
|
|
|
|
|
|
|
|
# Analyse des attributs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for statement in method.body:
|
|
|
|
|
if isinstance(statement, javalang.tree.MethodInvocation):
|
|
|
|
|
called_class = statement.qualifier.name if statement.qualifier else None
|
|
|
|
|
if called_class and called_class in self.class_names:
|
|
|
|
|
class_info['dependencies'].append(called_class)
|
|
|
|
|
|
|
|
|
|
for field in node.fields:
|
|
|
|
|
for declarator in field.declarators:
|
|
|
|
|
attribute_info = {
|
|
|
|
@ -60,8 +96,14 @@ class JavaAnalyzer(CodeAnalyzer):
|
|
|
|
|
'type': field.type.name
|
|
|
|
|
}
|
|
|
|
|
class_info['attributes'].append(attribute_info)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
attribute_type = field.type.name
|
|
|
|
|
if attribute_type not in ['int', 'double', 'String'] and attribute_type in self.class_names:
|
|
|
|
|
class_info['associations'].append(attribute_type)
|
|
|
|
|
|
|
|
|
|
self.classes.append(class_info)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Erreur lors de l'analyse du fichier {file_path}: {str(e)}")
|
|
|
|
|
|
|
|
|
@ -69,45 +111,110 @@ class JavaAnalyzer(CodeAnalyzer):
|
|
|
|
|
return self.classes
|
|
|
|
|
|
|
|
|
|
def get_relationships(self):
|
|
|
|
|
return self.relationships
|
|
|
|
|
uml_content = ""
|
|
|
|
|
for class_info in self.classes:
|
|
|
|
|
|
|
|
|
|
if class_info['extends'] and class_info['extends'] in self.class_names:
|
|
|
|
|
uml_content += f"{class_info['extends']} <|-- {class_info['name']}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for interface in class_info['implements']:
|
|
|
|
|
if interface in self.class_names:
|
|
|
|
|
uml_content += f"{interface} <|.. {class_info['name']}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['associations']:
|
|
|
|
|
for assoc_class in class_info['associations']:
|
|
|
|
|
uml_content += f"{class_info['name']} --> {assoc_class}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['dependencies']:
|
|
|
|
|
for dep_class in class_info['dependencies']:
|
|
|
|
|
uml_content += f"{class_info['name']} ..> {dep_class}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['aggregations']:
|
|
|
|
|
for agg_class in class_info['aggregations']:
|
|
|
|
|
uml_content += f"{class_info['name']} o--> {agg_class}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['compositions']:
|
|
|
|
|
for comp_class in class_info['compositions']:
|
|
|
|
|
uml_content += f"{class_info['name']} *--> {comp_class}\n"
|
|
|
|
|
|
|
|
|
|
return uml_content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CSharpAnalyzer(CodeAnalyzer):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.classes = []
|
|
|
|
|
self.class_names = set()
|
|
|
|
|
self.relationships = []
|
|
|
|
|
|
|
|
|
|
def analyze_directory(self, directory_path):
|
|
|
|
|
|
|
|
|
|
self._collect_class_names(directory_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return self._analyze_classes_and_generate_uml(directory_path)
|
|
|
|
|
|
|
|
|
|
def _collect_class_names(self, directory_path):
|
|
|
|
|
|
|
|
|
|
for root, _, files in os.walk(directory_path):
|
|
|
|
|
for file in files:
|
|
|
|
|
if file.endswith('.cs'):
|
|
|
|
|
try:
|
|
|
|
|
with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
|
|
|
|
|
content = f.read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class_pattern = r'class\s+(\w+)(?:\s*:\s*(\w+))?'
|
|
|
|
|
for match in re.finditer(class_pattern, content):
|
|
|
|
|
class_name = match.group(1)
|
|
|
|
|
self.class_names.add(class_name)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Erreur lors de la collecte des classes dans {file}: {str(e)}")
|
|
|
|
|
|
|
|
|
|
def _analyze_classes_and_generate_uml(self, directory_path):
|
|
|
|
|
|
|
|
|
|
for root, _, files in os.walk(directory_path):
|
|
|
|
|
for file in files:
|
|
|
|
|
if file.endswith('.cs'):
|
|
|
|
|
self._analyze_file(os.path.join(root, file))
|
|
|
|
|
return self.classes, self.relationships
|
|
|
|
|
|
|
|
|
|
return self.classes, self.get_relationships()
|
|
|
|
|
|
|
|
|
|
def _analyze_file(self, file_path):
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with open(file_path, 'r', encoding='utf-8') as file:
|
|
|
|
|
content = file.read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Analyse basique avec regex pour C#
|
|
|
|
|
# Note: Une analyse plus robuste nécessiterait un parser C# complet
|
|
|
|
|
class_pattern = r'class\s+(\w+)(?:\s*:\s*(\w+))?'
|
|
|
|
|
method_pattern = r'(?:public|private|protected)\s+(?:static\s+)?(\w+)\s+(\w+)\s*\((.*?)\)'
|
|
|
|
|
property_pattern = r'(?:public|private|protected)\s+(\w+)\s+(\w+)\s*{\s*get;\s*set;\s*}'
|
|
|
|
|
|
|
|
|
|
# Recherche des classes
|
|
|
|
|
|
|
|
|
|
for match in re.finditer(class_pattern, content):
|
|
|
|
|
class_name = match.group(1)
|
|
|
|
|
base_class = match.group(2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class_info = {
|
|
|
|
|
'name': class_name,
|
|
|
|
|
'methods': [],
|
|
|
|
|
'attributes': [],
|
|
|
|
|
'extends': base_class,
|
|
|
|
|
'implements': []
|
|
|
|
|
'implements': [],
|
|
|
|
|
'associations': [],
|
|
|
|
|
'dependencies': [],
|
|
|
|
|
'aggregations': [],
|
|
|
|
|
'compositions': []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.class_names.add(class_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Recherche des méthodes
|
|
|
|
|
for method_match in re.finditer(method_pattern, content):
|
|
|
|
|
method_info = {
|
|
|
|
|
'name': method_match.group(2),
|
|
|
|
@ -115,16 +222,162 @@ class CSharpAnalyzer(CodeAnalyzer):
|
|
|
|
|
'parameters': method_match.group(3).split(',') if method_match.group(3) else []
|
|
|
|
|
}
|
|
|
|
|
class_info['methods'].append(method_info)
|
|
|
|
|
|
|
|
|
|
# Recherche des propriétés
|
|
|
|
|
|
|
|
|
|
for prop_match in re.finditer(property_pattern, content):
|
|
|
|
|
attribute_info = {
|
|
|
|
|
'name': prop_match.group(2),
|
|
|
|
|
'type': prop_match.group(1)
|
|
|
|
|
}
|
|
|
|
|
class_info['attributes'].append(attribute_info)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
attribute_type = prop_match.group(1)
|
|
|
|
|
if attribute_type not in ['int', 'double', 'string'] and attribute_type in self.class_names:
|
|
|
|
|
class_info['associations'].append(attribute_type)
|
|
|
|
|
|
|
|
|
|
self.classes.append(class_info)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Erreur lors de l'analyse du fichier {file_path}: {str(e)}")
|
|
|
|
|
|
|
|
|
|
def get_classes(self):
|
|
|
|
|
return self.classes
|
|
|
|
|
|
|
|
|
|
def get_relationships(self):
|
|
|
|
|
uml_content = ""
|
|
|
|
|
for class_info in self.classes:
|
|
|
|
|
|
|
|
|
|
if class_info['extends'] and class_info['extends'] in self.class_names:
|
|
|
|
|
uml_content += f"{class_info['extends']} <|-- {class_info['name']}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for interface in class_info['implements']:
|
|
|
|
|
if interface in self.class_names:
|
|
|
|
|
uml_content += f"{interface} <|.. {class_info['name']}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['associations']:
|
|
|
|
|
for assoc_class in class_info['associations']:
|
|
|
|
|
uml_content += f"{class_info['name']} --> {assoc_class}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['dependencies']:
|
|
|
|
|
for dep_class in class_info['dependencies']:
|
|
|
|
|
uml_content += f"{class_info['name']} ..> {dep_class}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['aggregations']:
|
|
|
|
|
for agg_class in class_info['aggregations']:
|
|
|
|
|
uml_content += f"{class_info['name']} o--> {agg_class}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['compositions']:
|
|
|
|
|
for comp_class in class_info['compositions']:
|
|
|
|
|
uml_content += f"{class_info['name']} *--> {comp_class}\n"
|
|
|
|
|
|
|
|
|
|
return uml_content
|
|
|
|
|
|
|
|
|
|
class PHPAnalyzer(CodeAnalyzer):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.classes = []
|
|
|
|
|
self.class_names = set()
|
|
|
|
|
self.relationships = []
|
|
|
|
|
|
|
|
|
|
def analyze_directory(self, directory_path):
|
|
|
|
|
|
|
|
|
|
self._collect_class_names(directory_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return self._analyze_classes_and_generate_uml(directory_path)
|
|
|
|
|
|
|
|
|
|
def _collect_class_names(self, directory_path):
|
|
|
|
|
|
|
|
|
|
for root, _, files in os.walk(directory_path):
|
|
|
|
|
for file in files:
|
|
|
|
|
if file.endswith('.php'):
|
|
|
|
|
try:
|
|
|
|
|
with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
|
|
|
|
|
content = f.read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class_pattern = r'class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([^{]+))?'
|
|
|
|
|
for match in re.finditer(class_pattern, content):
|
|
|
|
|
class_name = match.group(1)
|
|
|
|
|
self.class_names.add(class_name)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Erreur lors de la collecte des classes dans {file}: {str(e)}")
|
|
|
|
|
|
|
|
|
|
def _analyze_classes_and_generate_uml(self, directory_path):
|
|
|
|
|
|
|
|
|
|
for root, _, files in os.walk(directory_path):
|
|
|
|
|
for file in files:
|
|
|
|
|
if file.endswith('.php'):
|
|
|
|
|
self._analyze_file(os.path.join(root, file))
|
|
|
|
|
|
|
|
|
|
return self.classes, self.get_relationships()
|
|
|
|
|
|
|
|
|
|
def _analyze_file(self, file_path):
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with open(file_path, 'r', encoding='utf-8') as file:
|
|
|
|
|
content = file.read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class_pattern = r'class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([^{]+))?'
|
|
|
|
|
method_pattern = r'(?:public|private|protected)?\s+function\s+(\w+)\s*\((.*?)\)(?:\s*:\s*(\??\w+))?'
|
|
|
|
|
property_pattern = r'(?:public|private|protected)\s+\$(\w+)(?:\s*:\s*(\w+))?'
|
|
|
|
|
|
|
|
|
|
for class_match in re.finditer(class_pattern, content):
|
|
|
|
|
class_name = class_match.group(1)
|
|
|
|
|
base_class = class_match.group(2)
|
|
|
|
|
implements = class_match.group(3).split(',') if class_match.group(3) else []
|
|
|
|
|
implements = [i.strip() for i in implements]
|
|
|
|
|
|
|
|
|
|
class_info = {
|
|
|
|
|
'name': class_name,
|
|
|
|
|
'methods': [],
|
|
|
|
|
'attributes': [],
|
|
|
|
|
'extends': base_class,
|
|
|
|
|
'implements': implements,
|
|
|
|
|
'associations': [],
|
|
|
|
|
'dependencies': [],
|
|
|
|
|
'aggregations': [],
|
|
|
|
|
'compositions': []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for method_match in re.finditer(method_pattern, content):
|
|
|
|
|
method_info = {
|
|
|
|
|
'name': method_match.group(1),
|
|
|
|
|
'parameters': [param.strip() for param in method_match.group(2).split(',') if param.strip()],
|
|
|
|
|
'return_type': method_match.group(3) if method_match.group(3) else 'void'
|
|
|
|
|
}
|
|
|
|
|
class_info['methods'].append(method_info)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
method_content = content[method_match.end():content.find('}', method_match.end())]
|
|
|
|
|
for class_name in self.class_names:
|
|
|
|
|
if f'new {class_name}' in method_content:
|
|
|
|
|
if class_name not in class_info['dependencies']:
|
|
|
|
|
class_info['dependencies'].append(class_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for prop_match in re.finditer(property_pattern, content):
|
|
|
|
|
prop_name = prop_match.group(1)
|
|
|
|
|
prop_type = prop_match.group(2) if prop_match.group(2) else 'mixed'
|
|
|
|
|
|
|
|
|
|
attribute_info = {
|
|
|
|
|
'name': prop_name,
|
|
|
|
|
'type': prop_type
|
|
|
|
|
}
|
|
|
|
|
class_info['attributes'].append(attribute_info)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if prop_type in self.class_names:
|
|
|
|
|
class_info['associations'].append(prop_type)
|
|
|
|
|
|
|
|
|
|
self.classes.append(class_info)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Erreur lors de l'analyse du fichier {file_path}: {str(e)}")
|
|
|
|
|
|
|
|
|
@ -132,4 +385,35 @@ class CSharpAnalyzer(CodeAnalyzer):
|
|
|
|
|
return self.classes
|
|
|
|
|
|
|
|
|
|
def get_relationships(self):
|
|
|
|
|
return self.relationships
|
|
|
|
|
uml_content = ""
|
|
|
|
|
for class_info in self.classes:
|
|
|
|
|
|
|
|
|
|
if class_info['extends'] and class_info['extends'] in self.class_names:
|
|
|
|
|
uml_content += f"{class_info['extends']} <|-- {class_info['name']}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for interface in class_info['implements']:
|
|
|
|
|
if interface in self.class_names:
|
|
|
|
|
uml_content += f"{interface} <|.. {class_info['name']}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['associations']:
|
|
|
|
|
for assoc_class in set(class_info['associations']):
|
|
|
|
|
uml_content += f"{class_info['name']} --> {assoc_class}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['dependencies']:
|
|
|
|
|
for dep_class in set(class_info['dependencies']):
|
|
|
|
|
uml_content += f"{class_info['name']} ..> {dep_class}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['aggregations']:
|
|
|
|
|
for agg_class in set(class_info['aggregations']):
|
|
|
|
|
uml_content += f"{class_info['name']} o--> {agg_class}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if class_info['compositions']:
|
|
|
|
|
for comp_class in set(class_info['compositions']):
|
|
|
|
|
uml_content += f"{class_info['name']} *--> {comp_class}\n"
|
|
|
|
|
|
|
|
|
|
return uml_content
|