diff --git a/src/main/java/com/hy/java/uct/cdtocode/mapper/CodeRelationMapper.java b/src/main/java/com/hy/java/uct/cdtocode/mapper/CodeRelationMapper.java index 5369adebb0ef050c5fe63c5090802d32d308333e..cd56b4b518c51911bb5cc339e6f04d4ad6043f08 100644 --- a/src/main/java/com/hy/java/uct/cdtocode/mapper/CodeRelationMapper.java +++ b/src/main/java/com/hy/java/uct/cdtocode/mapper/CodeRelationMapper.java @@ -3,7 +3,7 @@ package com.hy.java.uct.cdtocode.mapper; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -21,327 +21,477 @@ import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.hy.java.uct.cdtocode.util.Entity; import com.hy.java.uct.cdtocode.util.MappedFile; -import com.hy.java.uct.util.ImgRelation; +import com.hy.java.uct.util.EntityRelation; import com.hy.java.uct.util.UMLClass; import com.hy.java.utility.common.Pair; public class CodeRelationMapper { /** - * 针对类图中的类和代码中的类,基于同义词、词缀词典等语料库,定义基类名称匹配和关联关系的启发式匹配规则,研究基于模糊匹配技术的追踪关系建立方法,解决类名不一致、内容不一致的模型和代码元素的追踪问题: - *

- *

- * - * @param possible_classes_to_be_mapped_in_doc 键值对:<类名(图里的类名),类对象(UMLClass对象)> - * @param java_files 键值对:<类全称(包+类名),java文件地址(可用javaparser解析文件内容)> - * - * @return 根据名称相似度和关系启发式映射的classes + * 对扩展后的模型与代码进行匹配 */ - public static Map map(Map possible_classes_to_be_mapped_in_doc, Map java_files) { - Set key_set = possible_classes_to_be_mapped_in_doc.keySet(); + public static Map map(Map classShortName_classObj_mappedByDoc, Map classFullName_javaFileDir_map) { + Set ClsImg_shortName_set = classShortName_classObj_mappedByDoc.keySet(); /* - * 1、针对类图中的每个类,基于类的名称、与代码中的类文件进行字符串模糊匹配,从源码文件中选出可能映射的文件,存在UML_class.possible_java_files中 + * 针对每个包含文档信息的实体,将其追踪到代码中的类 + * + * 模糊匹配:根据Ent_doc的类名、属性、方法,匹配代码中可能追踪到的类Cls_code,并计算追踪概率Sim(Ent_doc,Cls_code) */ - for (String key : key_set) { - UMLClass UML_class = possible_classes_to_be_mapped_in_doc.get(key); - // 对每个UML_class,根据其title,找java_files中类全称的类名与其相似的java文件 - Set java_file_set = java_files.keySet(); - for (String java_file_full_name : java_file_set) { - if (titleSimilarWithFile(UML_class.getTitle(), java_file_full_name.substring(java_file_full_name.lastIndexOf(".") + 1))) { - UML_class.possible_java_files.add(java_file_full_name); + for (String ClsImg_shortName : ClsImg_shortName_set) { + UMLClass UML_class = classShortName_classObj_mappedByDoc.get(ClsImg_shortName); + // 对于每个UML_class,将其追踪到的每个Ent_doc,都追踪到代码(即Cls_code) + for (Entity mappedEnt : UML_class.mappedEnt_ls) { + // 先将Ent_doc追踪到Cls_code。记录每个追踪到的java文件的类全称,以及追踪概率Sim(Ent_doc,Cls_code) + mapEntToCode(mappedEnt, classFullName_javaFileDir_map); + // 然后对Ent_doc的每个related_Ent,同样将其追踪到Cls_code + for (EntityRelation relation : mappedEnt.relations_between_Ent) { + Entity related_Ent = relation.related_ent; + mapEntToCode(related_Ent, classFullName_javaFileDir_map); } } } + // 检查Sim(Ent_doc,Cls_code),可注释掉 + // check_SimEC(classShortName_classObj_mappedByDoc); /* - * 2、还要找与UML_class模糊匹配的类文件有继承、实现两类关系的其他类文件,如果其他类文件并未出现在类图中,则也应放入possible_java_files中 + * 基于关系推理的取舍 + * + * 如果模型中两个类具有某类型的关系,则查找两个类各自的“可能追踪到的类”之间,是否存在两个类也具有相同的关系。如果有,则认为两个代码中的类分别是“根据关系推理追踪到的类”。保留所有由推理获得的“可能追踪到的类” ,舍弃所有不能从关系推理到的类 * - * 这一段对性能影响最严重,可以抽成一个方法、并检查注释掉后对结果有多大影响。如果对结果影响不大,则可以去掉,提高性能 + * 注意:如果有关系,才做推理。如果原本就没有关系,则不做推理,将此处概率默认置为1 + * + * 由于对每个Ent_doc而言,其可能与多个Ent'_doc具有关系(即不止一个R),所以对其具有的每条关系R,都有一个“在关系R下从Ent_doc追踪到Cls_code的概率”PE_code (Ent_doc,Cls_code,R)=0.5×Sim(Ent_doc,Cls_code)+0.5×PR_doc(Ent_doc,Ent'_doc) + * + * 进行完这步后,对Cls_img的每个Ent_doc而言,会将其追踪到数个truly_mapped_file,每个truly_mapped_file均对应数个PE_code(Ent_doc,Cls_code,R) */ - // addOtherPossibleFiles(possible_classes_to_be_mapped_in_doc, java_files); + detectUMLCLsRelations(classShortName_classObj_mappedByDoc, classFullName_javaFileDir_map); + // 检查PE_code(Ent_doc,Cls_code,R),可注释掉 + // check_PEcodeR(classShortName_classObj_mappedByDoc); /* - * 3、然后针对UML_class的每条out_rela,记录其target_class。 + * 最终两部分概率合并即得图中每个类的追踪推荐列表(如果太多,则取前5,或者取概率>50%的) * - * 然后看UML_class.possible_java_files中的每个文件是否也有out_rela。 + * P(Cls_img,Cls_code)=PE_doc(Cls_img,Ent_doc)×PE_code(Ent_doc,Cls_code,R) * - * 如果UML_class“可能映射到的文件”有out_rela,且文件的out_rela目标文件被包含于target_class.possible_java_files中,则UML_class映射到该文件,且记录target_class和其映射文件。 + * 注意:目前,Cls_img→数个Ent_doc→数个Cls_code(每个都带着数个PEcode_R) */ - for (String key : key_set) { - UMLClass UML_class = possible_classes_to_be_mapped_in_doc.get(key); - if (UML_class.possible_java_files.size() < 1) { - // System.out.println(UML_class.getTitle() + "\t" + "没有映射"); - } else { - // 如果该UML_class没有out_rela,则possible_java_files就是映射结果 - if (UML_class.out_relas.isEmpty()) { - for (String possible_java_full_name : UML_class.possible_java_files) { - MappedFile mapped_file = new MappedFile(); - mapped_file.java_file_dir = java_files.get(possible_java_full_name); - mapped_file.ratio = 1.0; - UML_class.mapped_java_files.add(mapped_file); - } - } else { - // 针对UML_class的每条out_rela - for (ImgRelation out_rela : UML_class.out_relas) { - // 记录其target_class - UMLClass target_class = out_rela.target; - // 看possible_java_files中的每个文件是否也有out_rela,且文件的out_rela目标文件也在target_class的possible_java_files中 - for (String possible_java_full_name : UML_class.possible_java_files) { - String short_java_class_name = possible_java_full_name.substring(possible_java_full_name.lastIndexOf(".") + 1); - try { - CompilationUnit possible_java_file = StaticJavaParser.parse(new File(java_files.get(possible_java_full_name))); - // 文件的out_rela包括继承、实现、依赖(其引入的包)。聚合实质是内部类,只需遍历编译目录查找文件名即可 - Optional o_class = possible_java_file.getClassByName(short_java_class_name); - if (o_class.isPresent()) { - ClassOrInterfaceDeclaration possible_class = o_class.get(); - // 继承:文件的继承关系目标文件 - NodeList extended_types = possible_class.getExtendedTypes(); - for (ClassOrInterfaceType extended_class : extended_types) { - // 需获取继承关系目标文件的类全称 - String extended_class_full_name = getClassFullName(extended_class.asString(), possible_java_file.getImports(), possible_java_file.getPackageDeclaration()); - // 判断文件的out_rela目标类全称是否也在target_class的possible_java_files中 - Pair if_target_class_possible_files_contain_class = possibleFilesContainClass(target_class.possible_java_files, extended_class_full_name); - // 如果有,则UML_class映射到该文件,且记录target_class和其映射文件。 - if (if_target_class_possible_files_contain_class.getLeft()) { - // UML_class映射到这样一个文件,且有目标类target_class - MappedFile mapped_file = new MappedFile(); - mapped_file.java_file_dir = java_files.get(possible_java_full_name); - mapped_file.source_or_target_class_with_mapped_file = Pair.createPair(target_class, java_files.get(if_target_class_possible_files_contain_class.getRight())); - mapped_file.relation_type = "继承"; - // 这个概率可以根据图里的UML_class和文件里的possible_class的“属性、方法的相似度”做进一步计算 - mapped_file.ratio = 0.75 * getSimilarity(UML_class, possible_class) + 0.75 * out_rela.ratio; - UML_class.mapped_java_files.add(mapped_file); - // 目标类target_class正好也映射到一个文件上了。这会把一些只有进没有出的class也映射到 - MappedFile target_class_mapped_file = new MappedFile(); - target_class_mapped_file.java_file_dir = java_files.get(if_target_class_possible_files_contain_class.getRight()); - try { - // 解析target_class对应的java文件 - CompilationUnit target_class_mapped_java_file = StaticJavaParser.parse(new File(target_class_mapped_file.java_file_dir)); - // 文件的out_rela包括继承、实现、依赖(其引入的包)。聚合实质是内部类,只需遍历编译目录查找文件名即可 - Optional o_target_class = target_class_mapped_java_file.getClassByName(target_class.getTitle()); - if (o_target_class.isPresent()) { - ClassOrInterfaceDeclaration possible_target_class = o_target_class.get(); - target_class_mapped_file.ratio = 0.75 * getSimilarity(target_class, possible_target_class) + 0.75 * out_rela.ratio; - } - } catch (Exception e) { - target_class_mapped_file.ratio = out_rela.ratio; - } - target_class.mapped_java_files.add(target_class_mapped_file); - // System.out.println(UML_class.getTitle() + "(映射为" + - // java_files.get(possible_java_full_name) + ")继承了" + target_class.getTitle() + - // "(映射为" + - // java_files.get(if_target_class_possible_files_contain_class.getRight()) + - // ")"); - } - } - // 实现:文件的实现关系目标文件 - NodeList implemented_types = possible_class.getImplementedTypes(); - for (ClassOrInterfaceType implemented_class : implemented_types) { - // 需获取实现关系目标文件的类全称 - String implemented_class_full_name = getClassFullName(implemented_class.asString(), possible_java_file.getImports(), possible_java_file.getPackageDeclaration()); - // 判断文件的out_rela目标类全称是否也在target_class的possible_java_files中 - Pair if_target_class_possible_files_contain_class = possibleFilesContainClass(target_class.possible_java_files, implemented_class_full_name); - // 如果有,则UML_class映射到该文件,且记录target_class和其映射文件。 - if (if_target_class_possible_files_contain_class.getLeft()) { - // UML_class映射到这样一个文件,且有目标类target_class - MappedFile mapped_file = new MappedFile(); - mapped_file.java_file_dir = java_files.get(possible_java_full_name); - mapped_file.source_or_target_class_with_mapped_file = Pair.createPair(target_class, java_files.get(if_target_class_possible_files_contain_class.getRight())); - mapped_file.relation_type = "实现"; - // 这个概率可以根据图里的UML_class和文件里的possible_class的“属性、方法的相似度”做进一步计算 - mapped_file.ratio = 0.75 * getSimilarity(UML_class, possible_class) + 0.75 * out_rela.ratio; - UML_class.mapped_java_files.add(mapped_file); - // 目标类target_class正好也映射到一个文件上了。这会把一些只有进没有出的class也映射到 - MappedFile target_class_mapped_file = new MappedFile(); - target_class_mapped_file.java_file_dir = java_files.get(if_target_class_possible_files_contain_class.getRight()); - try { - // 解析target_class对应的java文件 - CompilationUnit target_class_mapped_java_file = StaticJavaParser.parse(new File(target_class_mapped_file.java_file_dir)); - // 文件的out_rela包括继承、实现、依赖(其引入的包)。聚合实质是内部类,只需遍历编译目录查找文件名即可 - Optional o_target_class = target_class_mapped_java_file.getClassByName(target_class.getTitle()); - if (o_target_class.isPresent()) { - ClassOrInterfaceDeclaration possible_target_class = o_target_class.get(); - target_class_mapped_file.ratio = 0.75 * getSimilarity(target_class, possible_target_class) + 0.75 * out_rela.ratio; - } - } catch (Exception e) { - target_class_mapped_file.ratio = out_rela.ratio; - } - target_class.mapped_java_files.add(target_class_mapped_file); - // System.out.println(UML_class.getTitle() + "(映射为" + - // java_files.get(possible_java_full_name) + ")实现了" + target_class.getTitle() + - // "(映射为" + - // java_files.get(if_target_class_possible_files_contain_class.getRight()) + - // ")"); - } - } - // 依赖:文件的依赖关系目标文件 - NodeList imports = possible_java_file.getImports(); - for (ImportDeclaration import_declaration : imports) { - // 需获取实现关系目标文件的类全称 - String denpended_class_full_name = import_declaration.toString().substring(import_declaration.toString().lastIndexOf(" ") + 1, import_declaration.toString().lastIndexOf(";")); - // 判断文件的out_rela目标类全称是否也在target_class的possible_java_files中 - Pair if_target_class_possible_files_contain_class = possibleFilesContainClass(target_class.possible_java_files, denpended_class_full_name); - // 如果有,则UML_class映射到该文件,且记录target_class和其映射文件。 - if (if_target_class_possible_files_contain_class.getLeft()) { - // UML_class映射到这样一个文件,且有目标类target_class - MappedFile mapped_file = new MappedFile(); - mapped_file.java_file_dir = java_files.get(possible_java_full_name); - mapped_file.source_or_target_class_with_mapped_file = Pair.createPair(target_class, java_files.get(if_target_class_possible_files_contain_class.getRight())); - mapped_file.relation_type = "依赖"; - // 这个概率可以根据图里的UML_class和文件里的possible_class的“属性、方法的相似度”做进一步计算 - mapped_file.ratio = 0.75 * getSimilarity(UML_class, possible_class) + 0.75 * out_rela.ratio; - UML_class.mapped_java_files.add(mapped_file); - // 目标类target_class正好也映射到一个文件上了。这会把一些只有进没有出的class也映射到 - MappedFile target_class_mapped_file = new MappedFile(); - target_class_mapped_file.java_file_dir = java_files.get(if_target_class_possible_files_contain_class.getRight()); - try { - // 解析target_class对应的java文件 - CompilationUnit target_class_mapped_java_file = StaticJavaParser.parse(new File(target_class_mapped_file.java_file_dir)); - // 文件的out_rela包括继承、实现、依赖(其引入的包)。聚合实质是内部类,只需遍历编译目录查找文件名即可 - Optional o_target_class = target_class_mapped_java_file.getClassByName(target_class.getTitle()); - if (o_target_class.isPresent()) { - ClassOrInterfaceDeclaration possible_target_class = o_target_class.get(); - target_class_mapped_file.ratio = 0.75 * getSimilarity(target_class, possible_target_class) + 0.75 * out_rela.ratio; - } - } catch (Exception e) { - target_class_mapped_file.ratio = out_rela.ratio; - } - target_class.mapped_java_files.add(target_class_mapped_file); - // System.out.println(UML_class.getTitle() + "(映射为" + - // java_files.get(possible_java_full_name) + ")依赖了" + target_class.getTitle() + - // "(映射为" + - // java_files.get(if_target_class_possible_files_contain_class.getRight()) + - // ")"); - } - } - } - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } + getInitTraceResults(classShortName_classObj_mappedByDoc); + // 此时UML_class.duplicated_mapped_javaFile_ls可能包含重复结果,所以还需过滤一遍 + filterTraceResults(classShortName_classObj_mappedByDoc); + return classShortName_classObj_mappedByDoc; + } + + /** + * 将Ent_doc追踪到Cls_code。记录每个追踪到的java文件的类全称,以及追踪概率Sim(Ent_doc,Cls_code) + */ + private static void mapEntToCode(Entity Ent_doc, Map classFullName_javaFileDir_map) { + Set ClsCode_fullName_set = classFullName_javaFileDir_map.keySet(); + // 遍历一遍全体java文件,找所有与Ent_doc相似的Cls_code + for (String ClsCode_fullName : ClsCode_fullName_set) { + // 对每个java文件对应的full name,获取其short name + String ClsCode_shortName = getClsShortNameFromFullName(ClsCode_fullName); + // 如果Ent_doc.name与Cls_code的short name相似,再进行属性、方法的比较,否则直接pass + if (EntName_SimilarWith_ClsShortName(Ent_doc.name, ClsCode_shortName)) { + // 此时Ent_doc.name与Cls_code的short name相似。暂时记录名称相似度 + double name_similarity = sim_EntDocName_ClsCodeName(Ent_doc.name, ClsCode_shortName); + // 然后进行属性、方法的比较 + try { + CompilationUnit Cls_code = StaticJavaParser.parse(new File(classFullName_javaFileDir_map.get(ClsCode_fullName))); + // 判断Ent_doc与Cls_code的属性、方法是否相似(即:Ent_doc的属性、方法完全被包含于Cls_code中) + Pair attriMethod_similarity_pair = EntAttriMethod_SimilarWith_ClsAttriMethod(Ent_doc, Cls_code, ClsCode_shortName); + // 如果Ent_doc与Cls_code的属性、方法相似,则将其追踪过去 + if (attriMethod_similarity_pair.getLeft() == true) { + // 追踪时记录Cls_code的full name,便于在map里查找 + // 追踪概率Sim(Ent_doc,Cls_code)=名称相似度*属性方法相似度 + Ent_doc.possibleMapped_javaFiles.add(Pair.createPair(ClsCode_fullName, name_similarity * attriMethod_similarity_pair.getRight())); } + } catch (FileNotFoundException e) { + e.printStackTrace(); } } } - // 4、无需做in_rela,因为遍历完所有类的out_rela后,所有关系就都遍历了。所以这一步应该根据UMLClass的功能解释,直接找java_files中名称相似、功能解释相似的java文件,存到UML_class.mapped_java_files中 - // 5、对possible_classes_to_be_mapped_in_doc中的UMLClass.mapped_java_files去重。对其映射到的每个文件,如果该映射没有关系目标、且有重复,则去重 - Set mapped_classes_key_set = possible_classes_to_be_mapped_in_doc.keySet(); - for (String mapped_classes_key : mapped_classes_key_set) { - UMLClass UML_class = possible_classes_to_be_mapped_in_doc.get(mapped_classes_key); - Set mapped_file_no_target = new HashSet<>(); - List mapped_file_has_target = new ArrayList<>(); - if (!UML_class.mapped_java_files.isEmpty()) { - for (MappedFile java_file_and_target_class : UML_class.mapped_java_files) { - if (java_file_and_target_class.source_or_target_class_with_mapped_file == null) { - mapped_file_no_target.add(java_file_and_target_class.java_file_dir); - } else { - mapped_file_has_target.add(java_file_and_target_class); + } + + /** + * 通过类全称,获得类短称 + */ + private static String getClsShortNameFromFullName(String clsCode_fullName) { + return clsCode_fullName.substring(clsCode_fullName.lastIndexOf(".") + 1); + } + + /** + * 对每个UML_class,根据其title,找java_files中类全称的类名与其相似的java文件 + */ + private static boolean EntName_SimilarWith_ClsShortName(String name1, String name2) { + // 如果两个String相等,则返回true + if (name1.equals(name2)) { + return true; + } + // 如果两个String相似,则返回true + else { + // 处理一下两个String,然后看它们是否相似 + String name1_lower = name1.toLowerCase(); + String name2_lower = name2.toLowerCase(); + // 判断处理后的两个String是否相似 + if (name2_lower.contains(name1_lower)) { + return true; + } + } + // 如果两个String既不相等也不相似,则返回false + return false; + } + + /** + * 判断Ent_doc与Cls_code的属性、方法是否相似(即:Ent_doc的属性、方法完全被包含于Cls_code中) + * + * @param ClsCode_shortName + */ + private static Pair EntAttriMethod_SimilarWith_ClsAttriMethod(Entity Ent_doc, CompilationUnit Cls_code, String ClsCode_shortName) { + Pair res = Pair.createPair(false, 0.0); + // 实际是用Ent_doc.parent_class的属性方法去与Cls_code对比 + UMLClass parent_cls = Ent_doc.parent_class; + // 如果Ent_doc有parent_cls,则去对比 + if (parent_cls != null) { + Optional o_class = Cls_code.getClassByName(ClsCode_shortName); + if (o_class.isPresent()) { + // 代码元素 + ClassOrInterfaceDeclaration clsCode_Unit = o_class.get(); + /* + * 正式比较 + */ + int UMLAttri_size = 0; + int UMLMethod_size = 0; + int mappedAttri_count = 0; + int mappedMethod_count = 0; + /* + * 先比较parent_cls的所有属性 + */ + if (parent_cls.getAttrisStr() != null) { + List UMLAttris = parseAttris(parent_cls.getAttrisStr()); + UMLAttri_size = UMLAttris.size(); + List ClsCode_fields = clsCode_Unit.getFields(); + // 对比UMLAttris和ClsCode_fields + for (String attri : UMLAttris) { + if (clsCodeUnit_Contains_UMLAttri(attri, ClsCode_fields)) { + mappedAttri_count++; + } } } - UML_class.mapped_java_files.clear(); - for (String file_dir : mapped_file_no_target) { - MappedFile mapped_file = new MappedFile(); - mapped_file.java_file_dir = java_files.get(file_dir); - mapped_file.ratio = 1.0; - UML_class.mapped_java_files.add(mapped_file); + /* + * 再比较parent_cls的所有方法 + */ + if (parent_cls.getMethodsStr() != null) { + List UMLMethods = parseMethods(parent_cls.getMethodsStr()); + UMLMethod_size = UMLMethods.size(); + List ClsCode_methods = clsCode_Unit.getMethods(); + // 对比UMLMethods和ClsCode_methods + for (String method : UMLMethods) { + if (clsCodeUnit_Contains_UMLMethod(method, ClsCode_methods)) { + mappedMethod_count++; + } + } + } + /* + * 最后计算Ent_doc与Cls_code的属性方法相似度 + */ + if (UMLAttri_size + UMLMethod_size != 0) { + if (mappedAttri_count + mappedMethod_count != 0) { + res.setLeft(true); + res.setRight((double) (mappedAttri_count + mappedMethod_count) / (double) (UMLAttri_size + UMLMethod_size)); + } + } + // 如果Ent_doc没有属性和方法,则给默认相似度1 + else { + res.setLeft(true); + res.setRight(1.0); + } + } + } + // 如果Ent_doc没有parent_cls,则说明它是通过规则找的related_Ent'_doc。这种的给默认相似度1 + else { + res.setLeft(true); + res.setRight(1.0); + } + return res; + } + + /** + * 根据UMLClass的属性字符串,获得其属性名列表 + */ + private static List parseAttris(String attrisStr) { + List res = new ArrayList<>(); + String[] attrisStrs = attrisStr.split("\n"); + for (String raw_attri_str : attrisStrs) { + if (!raw_attri_str.isBlank()) { + res.add(raw_attri_str.trim().split(":")[0].substring(1)); + } + } + return res; + } + + private static boolean clsCodeUnit_Contains_UMLAttri(String attri, List clsCode_fields) { + boolean res = false; + for (FieldDeclaration field : clsCode_fields) { + if (field.toString().contains(attri)) { + res = true; + break; + } + } + return res; + } + + /** + * 根据UMLClass的方法字符串,获得其方法名列表 + */ + private static List parseMethods(String methodsStr) { + List res = new ArrayList<>(); + String[] methodsStrs = methodsStr.split("\n"); + for (String raw_method_str : methodsStrs) { + if (!raw_method_str.isBlank()) { + res.add(raw_method_str.trim().split("\\(")[0].substring(1)); + } + } + return res; + } + + private static boolean clsCodeUnit_Contains_UMLMethod(String method, List clsCode_methods) { + boolean res = false; + for (MethodDeclaration clsCode_method : clsCode_methods) { + if (clsCode_method.getDeclarationAsString().contains(method)) { + res = true; + break; + } + } + return res; + } + + /** + * 计算两个字符串的Jaccard相似度 + */ + private static double sim_EntDocName_ClsCodeName(String string1, String string2) { + double result = 0.0; + // 两个字符串的字符集 + Set chars_of_string1 = string1.chars().boxed().collect(Collectors.toSet()); + Set chars_of_string2 = string2.chars().boxed().collect(Collectors.toSet()); + // 交集大小 + int intersection = SetUtils.intersection(chars_of_string1, chars_of_string2).size(); + // 并集大小 + int union = SetUtils.union(chars_of_string1, chars_of_string2).size(); + // Jaccard相似度 + if (union != 0) { + result = ((double) intersection) / ((double) union); + } + return result; + } + + /** + * 检查模糊匹配结果 + */ + private static void check_SimEC(Map classShortName_classObj_mappedByDoc) { + Set ClsImg_shortName_set = classShortName_classObj_mappedByDoc.keySet(); + for (String ClsImg_shortName : ClsImg_shortName_set) { + UMLClass UML_class = classShortName_classObj_mappedByDoc.get(ClsImg_shortName); + for (Entity mappedEnt : UML_class.mappedEnt_ls) { + for (Pair sim_EntDoc_ClsCode_pair : mappedEnt.possibleMapped_javaFiles) { + System.out.println("从" + ClsImg_shortName + "追踪到包含文档信息的实体" + mappedEnt.name + ",该实体有" + sim_EntDoc_ClsCode_pair.getRight() + "的概率追踪到代码" + sim_EntDoc_ClsCode_pair.getLeft()); } - UML_class.mapped_java_files.addAll(mapped_file_has_target); } } - return possible_classes_to_be_mapped_in_doc; } - /* - * 找与UML_class模糊匹配的类文件有继承、实现两类关系的其他类文件,如果其他类文件并未出现在类图中,则也应放入possible_java_files中 + /** + * 基于关系推理的取舍 * - * 由于这一段对性能影响最严重,所以抽成一个方法。最后检查注释掉后对结果有多大影响。如果对结果影响不大,则可以去掉,提高性能 + * 对每个Ent_doc,查其所有追踪到的Cls_code是否与Ent_doc.related_Ent有关系。如果一个Cls_code与某个由related_Ent追踪到的Cls_code'有同类关系,则该Cls_code就是“根据关系推理追踪到的代码文件”。 + * + * 保留所有由推理获得的“可能追踪到的类”(可以新存到一个list中),舍弃所有不能从关系推理到的类 */ - private static void addOtherPossibleFiles(Map possible_classes_to_be_mapped_in_doc, Map java_files) { - Set key_set = possible_classes_to_be_mapped_in_doc.keySet(); - for (String key : key_set) { - UMLClass UML_class = possible_classes_to_be_mapped_in_doc.get(key); - List extend_files = new ArrayList<>(); - List impl_files = new ArrayList<>(); - // 对每个模糊匹配UML_class的类文件,寻找与其有继承、实现两类关系的其他类文件 - for (String possible_java_full_name : UML_class.possible_java_files) { - String possible_java_file_short_name = possible_java_full_name.substring(possible_java_full_name.lastIndexOf(".") + 1); - // System.out.println(possible_java_file_short_name); - // 看谁继承或实现了它 - Set java_file_set = java_files.keySet(); - for (String java_file_full_class_name : java_file_set) { - String short_java_class_name = java_file_full_class_name.substring(java_file_full_class_name.lastIndexOf(".") + 1); + private static void detectUMLCLsRelations(Map classShortName_classObj_mappedByDoc, Map classFullName_javaFileDir_map) { + Set ClsImg_shortName_set = classShortName_classObj_mappedByDoc.keySet(); + for (String ClsImg_shortName : ClsImg_shortName_set) { + UMLClass UML_class = classShortName_classObj_mappedByDoc.get(ClsImg_shortName); + // 对每个Ent_doc,查其所有追踪到的Cls_code是否与Ent_doc.related_Ent有关系 + for (Entity Ent_doc : UML_class.mappedEnt_ls) { + for (Pair possibleMapped_javaFile_pair : Ent_doc.possibleMapped_javaFiles) { try { - CompilationUnit other_java_file = StaticJavaParser.parse(new File(java_files.get(java_file_full_class_name))); - Optional o_class = other_java_file.getClassByName(short_java_class_name); - if (o_class.isPresent()) { - ClassOrInterfaceDeclaration other_class = o_class.get(); - // 继承 - NodeList extended_types = other_class.getExtendedTypes(); - // 如果继承了模糊匹配的文件,且其他类文件并未出现在类图中,则也应放入possible_java_files中 - for (ClassOrInterfaceType extended_class : extended_types) { - // 需获取继承关系目标文件的类全称 - String extended_class_full_name = getClassFullName(extended_class.asString(), other_java_file.getImports(), other_java_file.getPackageDeclaration()); - if (extended_class_full_name.substring(extended_class_full_name.lastIndexOf(".") + 1).equals(possible_java_file_short_name)) { - if (!key_set.contains(short_java_class_name)) { - extend_files.add(java_file_full_class_name); - break; - } - } - } - // 实现:文件的实现关系目标文件 - NodeList implemented_types = other_class.getImplementedTypes(); - // 如果实现了模糊匹配的文件,且其他类文件并未出现在类图中,则也应放入possible_java_files中 - for (ClassOrInterfaceType implemented_class : implemented_types) { - // 需获取实现关系目标文件的类全称 - String implemented_class_full_name = getClassFullName(implemented_class.asString(), other_java_file.getImports(), other_java_file.getPackageDeclaration()); - if (implemented_class_full_name.substring(implemented_class_full_name.lastIndexOf(".") + 1).equals(possible_java_file_short_name)) { - if (!key_set.contains(short_java_class_name)) { - impl_files.add(java_file_full_class_name); - break; - } + CompilationUnit possibleMapped_ClsCode = StaticJavaParser.parse(new File(classFullName_javaFileDir_map.get(possibleMapped_javaFile_pair.getLeft()))); + String cls_shortName = getClsShortNameFromFullName(possibleMapped_javaFile_pair.getLeft()); + // 如果一个Cls_code与某个由related_Ent追踪到的Cls_code'有同类关系,则该Cls_code就是“根据关系推理追踪到的代码文件”。 + // 注意: 如果Ent_code没有related_Ent,则直接认为Ent_code能纯粹根据文本相似度追踪到Cls_code + Pair> ifTrulyMapped_possibleRs = check_if_trulyMapped(possibleMapped_ClsCode, cls_shortName, possibleMapped_javaFile_pair.getLeft(), Ent_doc.relations_between_Ent); + if (ifTrulyMapped_possibleRs.getLeft() == true) { + // 记录这个“根据关系推理追踪到的代码文件”。 + MappedFile truly_mapped_file_forEnt = new MappedFile(); + truly_mapped_file_forEnt.java_file_dir = classFullName_javaFileDir_map.get(possibleMapped_javaFile_pair.getLeft()); + // 记录在每条关系R下从Ent_doc追踪到Cls_code的概率PEcode_R + if (ifTrulyMapped_possibleRs.getRight().size() <= 0) { + // PE_code(Ent_doc,Cls_code,R) + // 如果Ent_code能纯粹根据文本相似度追踪到Cls_code,则将PEcode_R中的PR_doc部分置为默认值1 + double PEcode_R = 0.5 * possibleMapped_javaFile_pair.getRight() + 0.5 * 1.0; + truly_mapped_file_forEnt.PEcode_R_ls.add(Pair.createPair(null, PEcode_R)); + } else { + // 记录在每条关系R下从Ent_doc追踪到Cls_code的概率PEcode_R + for (EntityRelation R : ifTrulyMapped_possibleRs.getRight()) { + // PE_code(Ent_doc,Cls_code,R) + double PEcode_R = 0.5 * possibleMapped_javaFile_pair.getRight() + 0.5 * R.PR_doc; + truly_mapped_file_forEnt.PEcode_R_ls.add(Pair.createPair(R, PEcode_R)); } } + Ent_doc.trulyMapped_javaFiles.add(truly_mapped_file_forEnt); + // 进行到这儿后,对每个Ent_doc而言,会将其追踪到数个truly_mapped_file,每个truly_mapped_file均对应数个PE_code(Ent_doc,Cls_code,R) } } catch (FileNotFoundException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } } - UML_class.possible_java_files.addAll(extend_files); - UML_class.possible_java_files.addAll(impl_files); } } /** - * 对每个UML_class,根据其title,找java_files中类全称的类名与其相似的java文件 + * 如果一个Cls_code与某个由related_Ent追踪到的Cls_code'有同类关系,则该Cls_code就是“根据关系推理追踪到的代码文件” + * + * @param cls_shortName + * @param cls_fullName */ - private static boolean titleSimilarWithFile(String UML_class_title, String java_file_name) { - // 如果两个String相等,则返回true - if (UML_class_title.equals(java_file_name)) { - return true; + private static Pair> check_if_trulyMapped(CompilationUnit possibleMapped_ClsCode, String cls_shortName, String cls_fullName, List relations_between_Ent) { + Pair> res = Pair.createPair(false, new ArrayList<>()); + // 如果Ent_code没有related_Ent,则直接认为Ent_code能纯粹根据文本相似度追踪到Cls_code + if (relations_between_Ent.size() <= 0) { + res.setLeft(true); } - // 如果两个String相似,则返回true + // 如果Ent_code有related_Ent,则进行推理 else { - // 处理一下两个String,然后看它们是否相似 - String processed_java_file_name = java_file_name.toLowerCase(); - String processed_UML_class_title = UML_class_title.toLowerCase(); - // 判断处理后的两个String是否相似 - if (processed_java_file_name.contains(processed_UML_class_title)) { - return true; + // 对于每条关系,开始推理 + for (EntityRelation relation : relations_between_Ent) { + Entity related_Ent = relation.related_ent; + boolean truly_mapped = false; + // 根据关系类型推理一次即可 + switch (relation.relation_type) { + case "继承": { + truly_mapped = checkExtend(possibleMapped_ClsCode, related_Ent.possibleMapped_javaFiles, cls_shortName); + break; + } + case "实现": { + truly_mapped = checkImpl(possibleMapped_ClsCode, related_Ent.possibleMapped_javaFiles, cls_shortName); + break; + } + case "依赖": { + truly_mapped = checkDepend(possibleMapped_ClsCode, related_Ent.possibleMapped_javaFiles); + break; + } + case "聚合": { + truly_mapped = checkAggr(related_Ent.possibleMapped_javaFiles, cls_fullName); + break; + } + default: { + System.out.print("关系类型出错"); + break; + } + } + // 记录这条关系R + if (truly_mapped) { + res.getRight().add(relation); + } + } + if (res.getRight().size() > 0) { + res.setLeft(true); } } - // 如果两个String既不相等也不相似,则返回false - return false; + return res; + } + + /** + * 如果是继承关系,则判断继承目标是否能与某个related_Ent.mappedFile对上 + */ + private static boolean checkExtend(CompilationUnit possibleMapped_ClsCode, List> relatedEnt_possibleMapped_javaFiles, String cls_shortName) { + boolean res = false; + Optional o_class = possibleMapped_ClsCode.getClassByName(cls_shortName); + if (o_class.isPresent()) { + ClassOrInterfaceDeclaration clsCode_unit = o_class.get(); + // 继承:java文件的继承关系目标文件 + NodeList extend_targets = clsCode_unit.getExtendedTypes(); + // 对Cls_code的每个继承目标,检测是否能与某个related_Ent.mappedFile对上 + for (ClassOrInterfaceType extend_target : extend_targets) { + // 需获取继承目标的类全称 + String extendTarget_fullName = getTargetClassFullName(extend_target.asString(), possibleMapped_ClsCode.getImports(), possibleMapped_ClsCode.getPackageDeclaration()); + // 判断继承目标是否能与某个related_Ent.mappedFile对上 + Pair if_relatedEntMappedFiles_contain_extTarget = if_RelatedEntMappedFiles_contain_target(relatedEnt_possibleMapped_javaFiles, extendTarget_fullName); + if (if_relatedEntMappedFiles_contain_extTarget.getLeft() == true) { + // 只要有一个对上的就行 + res = true; + break; + } + } + } + return res; + } + + /** + * 如果是实现关系,则判断实现目标是否能与某个related_Ent.mappedFile对上 + */ + private static boolean checkImpl(CompilationUnit possibleMapped_ClsCode, List> relatedEnt_possibleMapped_javaFiles, String cls_shortName) { + boolean res = false; + Optional o_class = possibleMapped_ClsCode.getClassByName(cls_shortName); + if (o_class.isPresent()) { + ClassOrInterfaceDeclaration clsCode_unit = o_class.get(); + // 实现:java文件的实现关系目标文件 + NodeList impl_targets = clsCode_unit.getImplementedTypes(); + // 对Cls_code的每个实现目标,检测是否能与某个related_Ent.mappedFile对上 + for (ClassOrInterfaceType impl_target : impl_targets) { + // 需获取实现目标的类全称 + String implTarget_fullName = getTargetClassFullName(impl_target.asString(), possibleMapped_ClsCode.getImports(), possibleMapped_ClsCode.getPackageDeclaration()); + // 判断实现目标是否能与某个related_Ent.mappedFile对上 + Pair if_relatedEntMappedFiles_contain_implTarget = if_RelatedEntMappedFiles_contain_target(relatedEnt_possibleMapped_javaFiles, implTarget_fullName); + if (if_relatedEntMappedFiles_contain_implTarget.getLeft() == true) { + // 只要有一个对上的就行 + res = true; + break; + } + } + } + return res; } - private static String getClassFullName(String class_short_name, NodeList source_class_imports, Optional source_class_package_declaration) { + /** + * 如果是依赖关系,则判断依赖目标是否能与某个related_Ent.mappedFile对上 + */ + private static boolean checkDepend(CompilationUnit possibleMapped_ClsCode, List> relatedEnt_possibleMapped_javaFiles) { + boolean res = false; + // 依赖:java文件的依赖关系目标文件 + NodeList imports = possibleMapped_ClsCode.getImports(); + // 对Cls_code的每个依赖目标,检测是否能与某个related_Ent.mappedFile对上 + for (ImportDeclaration import_declaration : imports) { + // 需获取依赖目标的类全称 + String denpendTarget_fullName = import_declaration.toString().substring(import_declaration.toString().lastIndexOf(" ") + 1, import_declaration.toString().lastIndexOf(";")); + // 判断依赖目标是否能与某个related_Ent.mappedFile对上 + Pair if_relatedEntMappedFiles_contain_implTarget = if_RelatedEntMappedFiles_contain_target(relatedEnt_possibleMapped_javaFiles, denpendTarget_fullName); + if (if_relatedEntMappedFiles_contain_implTarget.getLeft() == true) { + // 只要有一个对上的就行 + res = true; + break; + } + } + return res; + } + + /** + * 如果是聚合关系,则判断聚合目标是否能与某个related_Ent.mappedFile对上 + */ + private static boolean checkAggr(List> relatedEnt_possibleMapped_javaFiles, String ClsCode_fullName) { + boolean res = false; + // 聚合的判定比较特殊:由于在基于模糊匹配的追踪中,Ent_doc只会追到外部类。所以,如果Cls_code的聚合目标(一定是它自己的一个内部类)能与某个related_Ent.mappedFile对上,则该mappedFile恰好是Cls_code自己 + // 所以,此处用于检查的fullName就是Cls_code自己的fullName + // 判断聚合目标是否能与某个related_Ent.mappedFile对上 + Pair if_relatedEntMappedFiles_contain_aggrTarget = if_RelatedEntMappedFiles_contain_target(relatedEnt_possibleMapped_javaFiles, ClsCode_fullName); + if (if_relatedEntMappedFiles_contain_aggrTarget.getLeft() == true) { + res = true; + } + return res; + } + + // 根据某个关系的目标类文件的短名、源类的包声明和包引用情况,获得目标类的全名 + private static String getTargetClassFullName(String class_short_name, NodeList source_class_imports, Optional source_class_package_declaration) { String result = null; // 对于内部类,只保留其父类名 if (class_short_name.contains(".")) { @@ -356,7 +506,7 @@ public class CodeRelationMapper { if (source_class_imports.isNonEmpty()) { for (ImportDeclaration import_declaration : source_class_imports) { String im_full_string = import_declaration.getNameAsString(); - String im_short_class_name = im_full_string.substring(im_full_string.lastIndexOf(".") + 1); + String im_short_class_name = getClsShortNameFromFullName(im_full_string); if (im_short_class_name.equals(class_short_name)) { // 直接将result替换为imports result = im_full_string; @@ -368,14 +518,14 @@ public class CodeRelationMapper { } /** - * 判断文件的out_rela目标文件是否也在target_class的possible_java_files中 + * 判断Cls_code的某个关系的目标是否能与某个Ent_doc.related_Ent.mappedFile对上 */ - private static Pair possibleFilesContainClass(List possible_java_classes_full_name, String out_rela_target_class_full_name) { + private static Pair if_RelatedEntMappedFiles_contain_target(List> relatedEnt_possibleMapped_javaFiles, String target_fullName) { Pair result = Pair.createPair(false, null); - for (String possible_java_file_full_name : possible_java_classes_full_name) { - if (possible_java_file_full_name.equals(out_rela_target_class_full_name)) { + for (Pair relatedEnt_possibleMapped_fullName : relatedEnt_possibleMapped_javaFiles) { + if (relatedEnt_possibleMapped_fullName.getLeft().equals(target_fullName)) { result.setLeft(true); - result.setRight(possible_java_file_full_name); + result.setRight(relatedEnt_possibleMapped_fullName.getLeft()); break; } } @@ -383,51 +533,99 @@ public class CodeRelationMapper { } /** - * 这个概率可以根据图里的UML_class和文件里的possible_class的“属性、方法的相似度”做进一步计算 + * 检查根据关系推理的结果 */ - private static double getSimilarity(UMLClass UML_class, ClassOrInterfaceDeclaration possible_class) { - double result = 0.0; - // System.out.println("======================================"); - // System.out.println(UML_class.getTitle()); - // System.out.println(possible_class.getNameAsString()); - String attris_methods = UML_class.getAttrisStr() + UML_class.getMethodsStr(); - String possible_attris_methods = ""; - List possible_class_fields = possible_class.getFields(); - for (FieldDeclaration fd : possible_class_fields) { - possible_attris_methods += fd.toString(); + private static void check_PEcodeR(Map classShortName_classObj_mappedByDoc) { + Set ClsImg_shortName_set = classShortName_classObj_mappedByDoc.keySet(); + for (String ClsImg_shortName : ClsImg_shortName_set) { + UMLClass UML_class = classShortName_classObj_mappedByDoc.get(ClsImg_shortName); + for (Entity Ent_doc : UML_class.mappedEnt_ls) { + for (MappedFile trulyMapped_javaFile : Ent_doc.trulyMapped_javaFiles) { + for (Pair PEcode_R : trulyMapped_javaFile.PEcode_R_ls) { + if (PEcode_R.getLeft() != null) { + System.out.println(UML_class.getTitle() + "追踪到的" + Ent_doc.name + "有" + PEcode_R.getRight() + "的概率追踪到代码中的" + trulyMapped_javaFile.java_file_dir + ",参考其与" + PEcode_R.getLeft().related_ent.name + "的" + PEcode_R.getLeft().relation_type + "关系"); + } else { + System.out.println(UML_class.getTitle() + "追踪到的" + Ent_doc.name + "有" + PEcode_R.getRight() + "的概率追踪到代码中的" + trulyMapped_javaFile.java_file_dir + ",这个实体是孤立的"); + } + } + } + } } - List possible_class_methods = possible_class.getMethods(); - for (MethodDeclaration md : possible_class_methods) { - possible_attris_methods += md.getNameAsString(); + } + + /** + * 合并“从图到文档”和“从文档到代码”的概率,即得图中每个类的追踪推荐列表(如果太多,则取前5,或者取概率>50%的) + * + * P(Cls_img,Cls_code)=PE_doc(Cls_img,Ent_doc)×PE_code(Ent_doc,Cls_code,R) + * + * 注意:目前,Cls_img→数个Ent_doc→数个Cls_code(每个都带着数个PEcode_R) + */ + private static void getInitTraceResults(Map classShortName_classObj_mappedByDoc) { + Set ClsImg_shortName_set = classShortName_classObj_mappedByDoc.keySet(); + for (String ClsImg_shortName : ClsImg_shortName_set) { + UMLClass UML_class = classShortName_classObj_mappedByDoc.get(ClsImg_shortName); + for (Entity Ent_doc : UML_class.mappedEnt_ls) { + for (MappedFile trulyMapped_javaFile_forEnt : Ent_doc.trulyMapped_javaFiles) { + MappedFile trulyMapped_javaFile_forCls = new MappedFile(); + trulyMapped_javaFile_forCls.java_file_dir = trulyMapped_javaFile_forEnt.java_file_dir; + for (Pair PEcode_R : trulyMapped_javaFile_forEnt.PEcode_R_ls) { + // P(Cls_img,Cls_code)=PE_doc(Cls_img,Ent_doc)×PE_code(Ent_doc,Cls_code,R) + double P_ClsImg_ClsCode = Ent_doc.PE_doc * PEcode_R.getRight(); + trulyMapped_javaFile_forCls.P_ls.add(Pair.createPair(PEcode_R.getLeft(), P_ClsImg_ClsCode)); + } + UML_class.duplicated_mapped_javaFile_ls.add(trulyMapped_javaFile_forCls); + } + } } - // Jaccard相似度 - Set chars_of_attris_methods = attris_methods.chars().boxed().collect(Collectors.toSet()); - Set chars_of_possible_attris_methods = possible_attris_methods.chars().boxed().collect(Collectors.toSet()); - // 交集数量 - int intersection = SetUtils.intersection(chars_of_attris_methods, chars_of_possible_attris_methods).size(); - // 并集数量 - int union = SetUtils.union(chars_of_attris_methods, chars_of_possible_attris_methods).size(); - if (union != 0) { - result = ((double) intersection) / ((double) union); + } + + /** + * 过滤追踪结果,使其无重复 + */ + private static void filterTraceResults(Map classShortName_classObj_mappedByDoc) { + Set ClsImg_shortName_set = classShortName_classObj_mappedByDoc.keySet(); + // 开始过滤 + for (String ClsImg_shortName : ClsImg_shortName_set) { + UMLClass UML_class = classShortName_classObj_mappedByDoc.get(ClsImg_shortName); + Map temp = new HashMap<>(); + for (MappedFile mapped_javaFile_inCls : UML_class.duplicated_mapped_javaFile_ls) { + if (!temp.containsKey(mapped_javaFile_inCls.java_file_dir)) { + temp.put(mapped_javaFile_inCls.java_file_dir, mapped_javaFile_inCls); + } else { + MappedFile mapped_javaFile_inTemp = temp.get(mapped_javaFile_inCls.java_file_dir); + // 对比mapped_javaFile_inTemp和mapped_javaFile_inCls,合并两者的“基于关系的追踪” + temp.put(mapped_javaFile_inTemp.java_file_dir, mergeTwoMappedFiles(mapped_javaFile_inTemp, mapped_javaFile_inCls)); + } + } + // 过滤完毕,将结果保存在UML_class.mapped_javaFile_ls中 + Set mappedFileDir_set = temp.keySet(); + for (String mappedFileDir : mappedFileDir_set) { + UML_class.mapped_javaFile_ls.add(temp.get(mappedFileDir)); + } } - // System.out.println(result); - return result; } + /** + * 对比mapped_javaFile_inTemp和mapped_javaFile_inCls,合并两者的“基于关系的追踪” + */ + private static MappedFile mergeTwoMappedFiles(MappedFile mapped_javaFile_inTemp, MappedFile mapped_javaFile_inCls) { + // TODO Auto-generated method stub + return null; + } + + /** + * 检查追踪结果 + */ public static void check(Map mapped_classes) { - Set key_set = mapped_classes.keySet(); - for (String key : key_set) { - System.out.println("=============================="); - UMLClass UML_class = mapped_classes.get(key); - if (UML_class.mapped_java_files.isEmpty()) { - System.out.println(UML_class.getTitle() + "没有映射"); - } else { - for (MappedFile java_file_target_class : UML_class.mapped_java_files) { - if (java_file_target_class.source_or_target_class_with_mapped_file != null) { - System.out.println(UML_class.getTitle() + "(映射为" + java_file_target_class.java_file_dir + ")" + java_file_target_class.relation_type + "了" + java_file_target_class.source_or_target_class_with_mapped_file.getLeft().getTitle() + "(映射为" - + java_file_target_class.source_or_target_class_with_mapped_file.getRight() + "),概率为" + java_file_target_class.ratio); + Set ClsImg_shortName_set = mapped_classes.keySet(); + for (String ClsImg_shortName : ClsImg_shortName_set) { + UMLClass UML_class = mapped_classes.get(ClsImg_shortName); + for (MappedFile mapped_javaFile : UML_class.mapped_javaFile_ls) { + for (Pair P : mapped_javaFile.P_ls) { + if (P.getLeft() != null) { + System.out.println(UML_class.getTitle() + "有" + P.getRight() + "的概率追踪到代码中的" + mapped_javaFile.java_file_dir + ",参考其与" + P.getLeft().related_ent.name + "的" + P.getLeft().relation_type + "关系。"); } else { - System.out.println(UML_class.getTitle() + "(映射为" + java_file_target_class.java_file_dir + "),概率为" + java_file_target_class.ratio); + System.out.println(UML_class.getTitle() + "有" + P.getRight() + "的概率追踪到代码中的" + mapped_javaFile.java_file_dir + ",这条追踪是没有相关关系的。"); } } } diff --git a/src/main/java/com/hy/java/uct/cdtocode/mapper/DocAnalyzer.java b/src/main/java/com/hy/java/uct/cdtocode/mapper/DocAnalyzer.java index 4a855b8449ba14c2cc484918938bde6bbedacb82..f42113cf0caa4f6a9f323e1e33a6e26fde8b6fcc 100644 --- a/src/main/java/com/hy/java/uct/cdtocode/mapper/DocAnalyzer.java +++ b/src/main/java/com/hy/java/uct/cdtocode/mapper/DocAnalyzer.java @@ -48,7 +48,7 @@ public class DocAnalyzer { } /** - * 基于启发式匹配规则(模糊匹配和词性分析)和概率的追踪 + * 基于启发式匹配规则(模糊匹配和词性分析)和概率的类图扩展 * * ①基于命名规范的模糊匹配、词性分析,将图中的类Cls_img追踪到文档中的Ent_doc,相似度为Sim(Cls_img,Ent_doc) * @@ -239,10 +239,10 @@ public class DocAnalyzer { Entity related_entity = new Entity(); related_entity.name = group[1]; entity_relation.related_ent = related_entity; - entity_relation.relation_type = group[2]; + entity_relation.relation_type = getRelationType(group[2]); // 追踪概率为R(Ent_doc,Ent'_doc),默认为1 entity_relation.R_Entdoc_OtherEntdoc = 1.0; - mappedEnt.entRela_ls.add(entity_relation); + mappedEnt.relations_between_Ent.add(entity_relation); } } } @@ -258,7 +258,7 @@ public class DocAnalyzer { // 每个Ent_doc for (Entity Ent_doc : Cls_img.mappedEnt_ls) { // 每条关系 - for (EntityRelation entity_relationship : Ent_doc.entRela_ls) { + for (EntityRelation entity_relationship : Ent_doc.relations_between_Ent) { Entity related_entity = entity_relationship.related_ent; // 形成一个正则表达式,用于匹配文档的每句话、查找至少包含这两个词其一的句子 String regex = "\\b(" + Ent_doc.name + "|" + related_entity.name + ")\\b"; @@ -288,7 +288,7 @@ public class DocAnalyzer { // 每个Ent_doc for (Entity Ent_doc : Cls_img.mappedEnt_ls) { // 每条关系 - for (EntityRelation entity_relationship : Ent_doc.entRela_ls) { + for (EntityRelation entity_relationship : Ent_doc.relations_between_Ent) { // Ent_doc与Ent'_doc具有关系的概率为PR_doc(Ent_doc,Ent'_doc) entity_relationship.PR_doc = entity_relationship.and_or * entity_relationship.R_Entdoc_OtherEntdoc; } @@ -308,19 +308,21 @@ public class DocAnalyzer { // 记录此关系:针对当前Ent_doc,检查related_Cls_img追踪的所有related_Ent是否在Ent_doc的关系中。如果有,则概率改为1;如果没有,则添加 for (Entity related_Ent : related_Cls_img.mappedEnt_ls) { boolean related = false; - for (EntityRelation entity_relationship : Ent_doc.entRela_ls) { + for (EntityRelation entity_relationship : Ent_doc.relations_between_Ent) { if (entity_relationship.related_ent.name.equals(related_Ent.name) && entity_relationship.relation_type.equals(img_relation.type)) { + // 如果有,则概率改为1 entity_relationship.PR_doc = 1.0; related = true; } } + // 如果没有,则添加 if (!related) { // 为该Ent_doc添加数条EntityRelation,每条关系都指向related_Cls_img追踪到的Ent_doc EntityRelation entity_relation = new EntityRelation(); entity_relation.related_ent = related_Ent; entity_relation.relation_type = img_relation.type; entity_relation.PR_doc = 1.0; - Ent_doc.entRela_ls.add(entity_relation); + Ent_doc.relations_between_Ent.add(entity_relation); } } } @@ -357,9 +359,10 @@ public class DocAnalyzer { } /** - * 检查PE_doc,即基于启发式匹配规则(模糊匹配和词性分析)和概率的追踪结果 + * 检查PE_doc,即基于启发式匹配规则(模糊匹配和词性分析)和概率的类图扩展结果 */ private static void check_PEdoc(Map classes_in_CD) { + System.out.println("============基于启发式匹配规则(模糊匹配和词性分析)和概率的类图扩展结果============"); Set classShortName_set = classes_in_CD.keySet(); for (String class_short_name : classShortName_set) { UMLClass Cls_img = classes_in_CD.get(class_short_name); @@ -369,6 +372,38 @@ public class DocAnalyzer { } } + /** + * 根据杜佳诺给出的“关系类型+关系描述”,分析出真正的关系类型,包括“继承”、“实现”、“依赖”、“聚合”四种 + */ + private static String getRelationType(String raw) { + String res = "依赖"; + // 该分隔符与杜佳诺商议 + String sep = ""; + String[] type_desc = raw.split(sep); + if (type_desc.length == 2) { + if (type_desc[1].contains("extend") || type_desc[1].contains("继承")) { + res = "继承"; + } + if (type_desc[1].contains("impl") || type_desc[1].contains("实现")) { + res = "实现"; + } + if (type_desc[1].contains("aggr") || type_desc[1].contains("聚合")) { + res = "聚合"; + } + } else { + if (type_desc[0].contains("extend") || type_desc[0].contains("继承")) { + res = "继承"; + } + if (type_desc[0].contains("impl") || type_desc[0].contains("实现")) { + res = "实现"; + } + if (type_desc[0].contains("aggr") || type_desc[0].contains("聚合")) { + res = "聚合"; + } + } + return res; + } + /** * 在所有设计文档中,根据类名查找相关语句,存到sentence_ls中 */ @@ -402,6 +437,7 @@ public class DocAnalyzer { * 检查PR_doc,即基于规则匹配和概率分析提取实体之间关系的追踪结果 */ private static void check_PRdoc(Map classes_in_CD) { + System.out.println("============基于规则匹配和概率分析提取实体之间关系的追踪结果============"); Set classShortName_set = classes_in_CD.keySet(); for (String class_short_name : classShortName_set) { // 每个Cls_img @@ -409,7 +445,7 @@ public class DocAnalyzer { // 每个Ent_doc for (Entity Ent_doc : Cls_img.mappedEnt_ls) { // 每条关系 - for (EntityRelation entity_relationship : Ent_doc.entRela_ls) { + for (EntityRelation entity_relationship : Ent_doc.relations_between_Ent) { System.out.println(class_short_name + "追踪到的" + Ent_doc.name + "与" + entity_relationship.related_ent.name + "有" + entity_relationship.PR_doc + "的概率具有" + entity_relationship.relation_type + "关系"); } } diff --git a/src/main/java/com/hy/java/uct/cdtocode/util/Entity.java b/src/main/java/com/hy/java/uct/cdtocode/util/Entity.java index 081f182b3241a4afe0fd21fc1aa2f591c036a574..c18d0ab78166cb05751603c6de2ab11477f720c5 100644 --- a/src/main/java/com/hy/java/uct/cdtocode/util/Entity.java +++ b/src/main/java/com/hy/java/uct/cdtocode/util/Entity.java @@ -5,16 +5,37 @@ import java.util.List; import com.hy.java.uct.util.EntityRelation; import com.hy.java.uct.util.UMLClass; +import com.hy.java.utility.common.Pair; public class Entity { public UMLClass parent_class; public String name; - // 基于启发式匹配规则(模糊匹配和词性分析)和概率的追踪 + /** + * 基于启发式匹配规则(模糊匹配和词性分析)和概率的追踪 + */ public double sim_ClsImg_EntDoc; public int F_N; public double Fn_Ndoc; + /** + * PE_doc(Cls_img,Ent_doc) + */ public double PE_doc; - // 基于规则匹配和概率分析提取实体之间关系 - public List entRela_ls = new ArrayList<>(); - // Entity往下会追到代码 + /** + * 基于规则匹配和概率分析提取实体之间关系 + */ + public List relations_between_Ent = new ArrayList<>(); + /** + * 对扩展后的模型与代码进行匹配时,根据模糊匹配获得的临时列表,形式为{@code List>} + *

+ * 追踪时记录Cls_code的full name,便于在map里查找 + *

+ * 追踪概率Sim(Ent_doc,Cls_code)=名称相似度*属性方法相似度 + */ + public List> possibleMapped_javaFiles = new ArrayList<>(); + /** + * 这个Entity所真正映射到的文件list(从possibleMapped_javaFiles中,根据关系推理而来) + *

+ * 每个映射保存了java文件地址和{@code List>} + */ + public List trulyMapped_javaFiles = new ArrayList<>(); } diff --git a/src/main/java/com/hy/java/uct/cdtocode/util/MappedFile.java b/src/main/java/com/hy/java/uct/cdtocode/util/MappedFile.java index bfff30af37546b42ebed9a14ecc4365fb1892e51..c4826c71d75e25b995ddaef2b0a9fee7ef838f16 100644 --- a/src/main/java/com/hy/java/uct/cdtocode/util/MappedFile.java +++ b/src/main/java/com/hy/java/uct/cdtocode/util/MappedFile.java @@ -1,20 +1,27 @@ package com.hy.java.uct.cdtocode.util; -import com.hy.java.uct.util.UMLClass; +import java.util.ArrayList; +import java.util.List; + +import com.hy.java.uct.util.EntityRelation; import com.hy.java.utility.common.Pair; /** * 一个类所真正映射到的文件 - * - * 每个映射形式为,关系类型>> */ public class MappedFile { // java文件地址 public String java_file_dir = null; - // <某种关系的目标或源UMLClass', 该关系下UMLClass'映射的文件> - public Pair source_or_target_class_with_mapped_file = null; - // 关系类型 - public String relation_type = null; - // 该映射的概率 - public double ratio = 0.0; + /** + * 在关系R下从Ent_doc追踪到Cls_code的概率 + *

+ * PE_code(Ent_doc,Cls_code,R)=0.5×Sim(Ent_doc,Cls_code)+0.5×PR_doc(Ent_doc,Ent'_doc) + */ + public List> PEcode_R_ls = new ArrayList<>(); + /** + * 在关系R下从Cls_img追踪到Cls_code的概率 + *

+ * P(Cls_img,Cls_code)=PE_doc(Cls_img,Ent_doc)×PE_code(Ent_doc,Cls_code,R) + */ + public List> P_ls = new ArrayList<>(); } diff --git a/src/main/java/com/hy/java/uct/util/EntityRelation.java b/src/main/java/com/hy/java/uct/util/EntityRelation.java index d796d6d9cb4f90b2a103e5e7f85c3d3f248daa8f..c5c82838b4c19acfb57021158b0c8c713a4ab463 100644 --- a/src/main/java/com/hy/java/uct/util/EntityRelation.java +++ b/src/main/java/com/hy/java/uct/util/EntityRelation.java @@ -2,10 +2,16 @@ package com.hy.java.uct.util; import com.hy.java.uct.cdtocode.util.Entity; +/** + * Ent_doc与Ent_doc'具有关系(类型为relation_type)的概率为PR_doc + */ public class EntityRelation { public Entity related_ent = null; public String relation_type = ""; public double R_Entdoc_OtherEntdoc = 0.0; public double and_or = 0.001; + /** + * PR_doc(Ent_doc,Ent'_doc) + */ public double PR_doc = 0.0; } diff --git a/src/main/java/com/hy/java/uct/util/UMLClass.java b/src/main/java/com/hy/java/uct/util/UMLClass.java index 475cc3ac950697259aec58d62c0ef133f768487d..eb53a305ebd8a8c1778b7390f95322f8008559bc 100644 --- a/src/main/java/com/hy/java/uct/util/UMLClass.java +++ b/src/main/java/com/hy/java/uct/util/UMLClass.java @@ -44,18 +44,20 @@ public class UMLClass { public Set mappedEnt_names = new HashSet<>(); /** - * java_files中类名与this.title相似的java文件 - * - * 从这些文件中去找真正的映射(由于抽象层次不同,所以可能不止一个java文件是真正的映射)。寻找时要参考关系网 + * 这个类所真正映射到的文件list(由于抽象层次不同,所以可能不止一个java文件是真正的映射) + *

+ * 每个映射保存了java文件地址和P(Cls_img,Cls_code) + *

+ * 这个List里可能会有重复的结果,所以还需要过滤 */ - public List possible_java_files = new ArrayList<>(); + public List duplicated_mapped_javaFile_ls = new ArrayList<>(); /** * 这个类所真正映射到的文件list(由于抽象层次不同,所以可能不止一个java文件是真正的映射) - * - * 每个映射形式为,关系类型>> + *

+ * 每个映射保存了java文件地址和P(Cls_img,Cls_code) */ - public List mapped_java_files = new ArrayList<>(); + public List mapped_javaFile_ls = new ArrayList<>(); public String getTitle() { return title; diff --git a/src/main/resources/cdtocode/doc/basic-architecture-simEnts.txt b/src/main/resources/cdtocode/doc/basic-architecture-simEnts.txt index 490efa0945e68bb01b3ea0391ab7225315830511..22a14092ae0139ecd5a970f1c72d7a82e19ec961 100644 --- a/src/main/resources/cdtocode/doc/basic-architecture-simEnts.txt +++ b/src/main/resources/cdtocode/doc/basic-architecture-simEnts.txt @@ -1,3 +1,4 @@ License EclipseLicense +Connector Connector Connector Connection Connector ServerConnector \ No newline at end of file diff --git a/src/test/java/com/hy/java/uct/cdtocode/CodeRelationMapperTest.java b/src/test/java/com/hy/java/uct/cdtocode/CodeRelationMapperTest.java index 34855befd87352a1c2daa148cc408872b494ca80..2c146e9ea10961e73fcf63ca0b06d02953c04cbb 100644 --- a/src/test/java/com/hy/java/uct/cdtocode/CodeRelationMapperTest.java +++ b/src/test/java/com/hy/java/uct/cdtocode/CodeRelationMapperTest.java @@ -27,6 +27,7 @@ import com.hy.java.uct.cdtocode.reader.CodeReader; import com.hy.java.uct.cdtocode.util.MappedFile; import com.hy.java.uct.util.ImgRelation; import com.hy.java.uct.util.UMLClass; +import com.hy.java.utility.common.FileEditor; import com.hy.java.utility.common.Pair; public class CodeRelationMapperTest { @@ -40,278 +41,17 @@ public class CodeRelationMapperTest { */ private static final String code_dir = System.getProperty("user.dir") + "\\src\\main\\resources\\cdtocode\\code\\"; - public static Map map(Map possible_classes_to_be_mapped_in_doc, Map java_files) { - Set key_set = possible_classes_to_be_mapped_in_doc.keySet(); - /* - * 1、针对类图中的每个类,基于类的名称、与代码中的类文件进行字符串模糊匹配,从源码文件中选出可能映射的文件,存在UML_class.possible_java_files中 - */ - for (String key : key_set) { - UMLClass UML_class = possible_classes_to_be_mapped_in_doc.get(key); - // 对每个UML_class,根据其title,找java_files中类全称的类名与其相似的java文件 - Set java_file_set = java_files.keySet(); - for (String java_file_full_name : java_file_set) { - if (titleSimilarWithFile(UML_class.getTitle(), java_file_full_name.substring(java_file_full_name.lastIndexOf(".") + 1))) { - UML_class.possible_java_files.add(java_file_full_name); - } - } - } - /* - * 2、还要找与UML_class模糊匹配的类文件有继承、实现两类关系的其他类文件,如果其他类文件并未出现在类图中,则也应放入possible_java_files中 - * - * 这一段对性能影响最严重,可以抽成一个方法、并检查注释掉后对结果有多大影响。如果对结果影响不大,则可以去掉,提高性能 - */ - // addOtherPossibleFiles(possible_classes_to_be_mapped_in_doc, java_files); - /* - * 3、然后针对UML_class的每条out_rela,记录其target_class。 - * - * 然后看UML_class.possible_java_files中的每个文件是否也有out_rela。 - * - * 如果UML_class“可能映射到的文件”有out_rela,且文件的out_rela目标文件被包含于target_class.possible_java_files中,则UML_class映射到该文件,且记录target_class和其映射文件。 - */ - for (String key : key_set) { - UMLClass UML_class = possible_classes_to_be_mapped_in_doc.get(key); - if (UML_class.possible_java_files.size() < 1) { - // System.out.println(UML_class.getTitle() + "\t" + "没有映射"); - } else { - // 如果该UML_class没有out_rela,则possible_java_files就是映射结果 - if (UML_class.out_relas.isEmpty()) { - for (String possible_java_full_name : UML_class.possible_java_files) { - MappedFile mapped_file = new MappedFile(); - mapped_file.java_file_dir = java_files.get(possible_java_full_name); - mapped_file.ratio = 1.0; - UML_class.mapped_java_files.add(mapped_file); - } - } else { - // 针对UML_class的每条out_rela - for (ImgRelation out_rela : UML_class.out_relas) { - // 记录其target_class - UMLClass target_class = out_rela.target; - // 看possible_java_files中的每个文件是否也有out_rela,且文件的out_rela目标文件也在target_class的possible_java_files中 - for (String possible_java_full_name : UML_class.possible_java_files) { - String short_java_class_name = possible_java_full_name.substring(possible_java_full_name.lastIndexOf(".") + 1); - try { - CompilationUnit possible_java_file = StaticJavaParser.parse(new File(java_files.get(possible_java_full_name))); - // 文件的out_rela包括继承、实现、依赖(其引入的包)。聚合实质是内部类,只需遍历编译目录查找文件名即可 - Optional o_class = possible_java_file.getClassByName(short_java_class_name); - if (o_class.isPresent()) { - ClassOrInterfaceDeclaration possible_class = o_class.get(); - // 继承:文件的继承关系目标文件 - NodeList extended_types = possible_class.getExtendedTypes(); - for (ClassOrInterfaceType extended_class : extended_types) { - // 需获取继承关系目标文件的类全称 - String extended_class_full_name = getClassFullName(extended_class.asString(), possible_java_file.getImports(), possible_java_file.getPackageDeclaration()); - // 判断文件的out_rela目标类全称是否也在target_class的possible_java_files中 - Pair if_target_class_possible_files_contain_class = possibleFilesContainClass(target_class.possible_java_files, extended_class_full_name); - // 如果有,则UML_class映射到该文件,且记录target_class和其映射文件。 - if (if_target_class_possible_files_contain_class.getLeft()) { - // UML_class映射到这样一个文件,且有目标类target_class - MappedFile mapped_file = new MappedFile(); - mapped_file.java_file_dir = java_files.get(possible_java_full_name); - mapped_file.source_or_target_class_with_mapped_file = Pair.createPair(target_class, java_files.get(if_target_class_possible_files_contain_class.getRight())); - mapped_file.relation_type = "继承"; - // 这个概率可以根据图里的UML_class和文件里的possible_class的“属性、方法的相似度”做进一步计算 - mapped_file.ratio = getSimilarity(UML_class, possible_class) + out_rela.ratio; - UML_class.mapped_java_files.add(mapped_file); - // 目标类target_class正好也映射到一个文件上了。这会把一些只有进没有出的class也映射到 - MappedFile target_class_mapped_file = new MappedFile(); - target_class_mapped_file.java_file_dir = java_files.get(if_target_class_possible_files_contain_class.getRight()); - try { - // 解析target_class对应的java文件 - CompilationUnit target_class_mapped_java_file = StaticJavaParser.parse(new File(target_class_mapped_file.java_file_dir)); - // 文件的out_rela包括继承、实现、依赖(其引入的包)。聚合实质是内部类,只需遍历编译目录查找文件名即可 - Optional o_target_class = target_class_mapped_java_file.getClassByName(target_class.getTitle()); - if (o_target_class.isPresent()) { - ClassOrInterfaceDeclaration possible_target_class = o_target_class.get(); - target_class_mapped_file.ratio = getSimilarity(target_class, possible_target_class) + out_rela.ratio; - } - } catch (Exception e) { - target_class_mapped_file.ratio = out_rela.ratio; - } - target_class.mapped_java_files.add(target_class_mapped_file); - // System.out.println(UML_class.getTitle() + "(映射为" + - // java_files.get(possible_java_full_name) + ")继承了" + target_class.getTitle() + - // "(映射为" + - // java_files.get(if_target_class_possible_files_contain_class.getRight()) + - // ")"); - } - } - // 实现:文件的实现关系目标文件 - NodeList implemented_types = possible_class.getImplementedTypes(); - for (ClassOrInterfaceType implemented_class : implemented_types) { - // 需获取实现关系目标文件的类全称 - String implemented_class_full_name = getClassFullName(implemented_class.asString(), possible_java_file.getImports(), possible_java_file.getPackageDeclaration()); - // 判断文件的out_rela目标类全称是否也在target_class的possible_java_files中 - Pair if_target_class_possible_files_contain_class = possibleFilesContainClass(target_class.possible_java_files, implemented_class_full_name); - // 如果有,则UML_class映射到该文件,且记录target_class和其映射文件。 - if (if_target_class_possible_files_contain_class.getLeft()) { - // UML_class映射到这样一个文件,且有目标类target_class - MappedFile mapped_file = new MappedFile(); - mapped_file.java_file_dir = java_files.get(possible_java_full_name); - mapped_file.source_or_target_class_with_mapped_file = Pair.createPair(target_class, java_files.get(if_target_class_possible_files_contain_class.getRight())); - mapped_file.relation_type = "实现"; - // 这个概率可以根据图里的UML_class和文件里的possible_class的“属性、方法的相似度”做进一步计算 - mapped_file.ratio = getSimilarity(UML_class, possible_class) + out_rela.ratio; - UML_class.mapped_java_files.add(mapped_file); - // 目标类target_class正好也映射到一个文件上了。这会把一些只有进没有出的class也映射到 - MappedFile target_class_mapped_file = new MappedFile(); - target_class_mapped_file.java_file_dir = java_files.get(if_target_class_possible_files_contain_class.getRight()); - try { - // 解析target_class对应的java文件 - CompilationUnit target_class_mapped_java_file = StaticJavaParser.parse(new File(target_class_mapped_file.java_file_dir)); - // 文件的out_rela包括继承、实现、依赖(其引入的包)。聚合实质是内部类,只需遍历编译目录查找文件名即可 - Optional o_target_class = target_class_mapped_java_file.getClassByName(target_class.getTitle()); - if (o_target_class.isPresent()) { - ClassOrInterfaceDeclaration possible_target_class = o_target_class.get(); - target_class_mapped_file.ratio = getSimilarity(target_class, possible_target_class) + out_rela.ratio; - } - } catch (Exception e) { - target_class_mapped_file.ratio = out_rela.ratio; - } - target_class.mapped_java_files.add(target_class_mapped_file); - // System.out.println(UML_class.getTitle() + "(映射为" + - // java_files.get(possible_java_full_name) + ")实现了" + target_class.getTitle() + - // "(映射为" + - // java_files.get(if_target_class_possible_files_contain_class.getRight()) + - // ")"); - } - } - // 依赖:文件的依赖关系目标文件 - NodeList imports = possible_java_file.getImports(); - for (ImportDeclaration import_declaration : imports) { - // 需获取实现关系目标文件的类全称 - String denpended_class_full_name = import_declaration.toString().substring(import_declaration.toString().lastIndexOf(" ") + 1, import_declaration.toString().lastIndexOf(";")); - // 判断文件的out_rela目标类全称是否也在target_class的possible_java_files中 - Pair if_target_class_possible_files_contain_class = possibleFilesContainClass(target_class.possible_java_files, denpended_class_full_name); - // 如果有,则UML_class映射到该文件,且记录target_class和其映射文件。 - if (if_target_class_possible_files_contain_class.getLeft()) { - // UML_class映射到这样一个文件,且有目标类target_class - MappedFile mapped_file = new MappedFile(); - mapped_file.java_file_dir = java_files.get(possible_java_full_name); - mapped_file.source_or_target_class_with_mapped_file = Pair.createPair(target_class, java_files.get(if_target_class_possible_files_contain_class.getRight())); - mapped_file.relation_type = "依赖"; - // 这个概率可以根据图里的UML_class和文件里的possible_class的“属性、方法的相似度”做进一步计算 - mapped_file.ratio = getSimilarity(UML_class, possible_class) + out_rela.ratio; - UML_class.mapped_java_files.add(mapped_file); - // 目标类target_class正好也映射到一个文件上了。这会把一些只有进没有出的class也映射到 - MappedFile target_class_mapped_file = new MappedFile(); - target_class_mapped_file.java_file_dir = java_files.get(if_target_class_possible_files_contain_class.getRight()); - try { - // 解析target_class对应的java文件 - CompilationUnit target_class_mapped_java_file = StaticJavaParser.parse(new File(target_class_mapped_file.java_file_dir)); - // 文件的out_rela包括继承、实现、依赖(其引入的包)。聚合实质是内部类,只需遍历编译目录查找文件名即可 - Optional o_target_class = target_class_mapped_java_file.getClassByName(target_class.getTitle()); - if (o_target_class.isPresent()) { - ClassOrInterfaceDeclaration possible_target_class = o_target_class.get(); - target_class_mapped_file.ratio = getSimilarity(target_class, possible_target_class) + out_rela.ratio; - } - } catch (Exception e) { - target_class_mapped_file.ratio = out_rela.ratio; - } - target_class.mapped_java_files.add(target_class_mapped_file); - // System.out.println(UML_class.getTitle() + "(映射为" + - // java_files.get(possible_java_full_name) + ")依赖了" + target_class.getTitle() + - // "(映射为" + - // java_files.get(if_target_class_possible_files_contain_class.getRight()) + - // ")"); - } - } - } - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - } - } - } - // 4、无需做in_rela,因为遍历完所有类的out_rela后,所有关系就都遍历了。所以这一步应该根据UMLClass的功能解释,直接找java_files中名称相似、功能解释相似的java文件,存到UML_class.mapped_java_files中 - // 5、对possible_classes_to_be_mapped_in_doc中的UMLClass.mapped_java_files去重。对其映射到的每个文件,如果该映射没有关系目标、且有重复,则去重 - Set mapped_classes_key_set = possible_classes_to_be_mapped_in_doc.keySet(); - for (String mapped_classes_key : mapped_classes_key_set) { - UMLClass UML_class = possible_classes_to_be_mapped_in_doc.get(mapped_classes_key); - Set mapped_file_no_target = new HashSet<>(); - List mapped_file_has_target = new ArrayList<>(); - if (!UML_class.mapped_java_files.isEmpty()) { - for (MappedFile java_file_and_target_class : UML_class.mapped_java_files) { - if (java_file_and_target_class.source_or_target_class_with_mapped_file == null) { - mapped_file_no_target.add(java_file_and_target_class.java_file_dir); - } else { - mapped_file_has_target.add(java_file_and_target_class); - } - } - UML_class.mapped_java_files.clear(); - for (String file_dir : mapped_file_no_target) { - MappedFile mapped_file = new MappedFile(); - mapped_file.java_file_dir = java_files.get(file_dir); - mapped_file.ratio = 1.0; - UML_class.mapped_java_files.add(mapped_file); - } - UML_class.mapped_java_files.addAll(mapped_file_has_target); - } - } - return possible_classes_to_be_mapped_in_doc; - } - - /* - * 找与UML_class模糊匹配的类文件有继承、实现两类关系的其他类文件,如果其他类文件并未出现在类图中,则也应放入possible_java_files中 - * - * 由于这一段对性能影响最严重,所以抽成一个方法。最后检查注释掉后对结果有多大影响。如果对结果影响不大,则可以去掉,提高性能 + /** + * 测试数据路径 */ - private static void addOtherPossibleFiles(Map possible_classes_to_be_mapped_in_doc, Map java_files) { - Set key_set = possible_classes_to_be_mapped_in_doc.keySet(); - for (String key : key_set) { - UMLClass UML_class = possible_classes_to_be_mapped_in_doc.get(key); - List extend_files = new ArrayList<>(); - List impl_files = new ArrayList<>(); - // 对每个模糊匹配UML_class的类文件,寻找与其有继承、实现两类关系的其他类文件 - for (String possible_java_full_name : UML_class.possible_java_files) { - String possible_java_file_short_name = possible_java_full_name.substring(possible_java_full_name.lastIndexOf(".") + 1); - // System.out.println(possible_java_file_short_name); - // 看谁继承或实现了它 - Set java_file_set = java_files.keySet(); - for (String java_file_full_class_name : java_file_set) { - String short_java_class_name = java_file_full_class_name.substring(java_file_full_class_name.lastIndexOf(".") + 1); - try { - CompilationUnit other_java_file = StaticJavaParser.parse(new File(java_files.get(java_file_full_class_name))); - Optional o_class = other_java_file.getClassByName(short_java_class_name); - if (o_class.isPresent()) { - ClassOrInterfaceDeclaration other_class = o_class.get(); - // 继承 - NodeList extended_types = other_class.getExtendedTypes(); - // 如果继承了模糊匹配的文件,且其他类文件并未出现在类图中,则也应放入possible_java_files中 - for (ClassOrInterfaceType extended_class : extended_types) { - // 需获取继承关系目标文件的类全称 - String extended_class_full_name = getClassFullName(extended_class.asString(), other_java_file.getImports(), other_java_file.getPackageDeclaration()); - if (extended_class_full_name.substring(extended_class_full_name.lastIndexOf(".") + 1).equals(possible_java_file_short_name)) { - if (!key_set.contains(short_java_class_name)) { - extend_files.add(java_file_full_class_name); - break; - } - } - } - // 实现:文件的实现关系目标文件 - NodeList implemented_types = other_class.getImplementedTypes(); - // 如果实现了模糊匹配的文件,且其他类文件并未出现在类图中,则也应放入possible_java_files中 - for (ClassOrInterfaceType implemented_class : implemented_types) { - // 需获取实现关系目标文件的类全称 - String implemented_class_full_name = getClassFullName(implemented_class.asString(), other_java_file.getImports(), other_java_file.getPackageDeclaration()); - if (implemented_class_full_name.substring(implemented_class_full_name.lastIndexOf(".") + 1).equals(possible_java_file_short_name)) { - if (!key_set.contains(short_java_class_name)) { - impl_files.add(java_file_full_class_name); - break; - } - } - } - } - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - UML_class.possible_java_files.addAll(extend_files); - UML_class.possible_java_files.addAll(impl_files); + private static final String test_data_dir = System.getProperty("user.dir") + "\\src\\test\\resources\\"; + + @Test + public void parseAttri() { + FileEditor fe = new FileEditor(test_data_dir + "attri"); + String[] strs = fe.readFileToString().split("\n"); + for (String s : strs) { + System.out.println("哈喽" + s.trim().split(":")[0].substring(1)); } } @@ -409,20 +149,4 @@ public class CodeRelationMapperTest { // System.out.println(result); return result; } - - public static void main(String[] args) { - /* - * 1、读取模型信息 - */ - // 读取完UML图识别结果后,将实体信息保存在classes_in_CD里 - Map classes_in_CD = CDReader.read(cd_dir + "cd-eclipse_jetty.txt"); - /* - * 2、读取code path指定的目录下所有java文件 - */ - Map java_files = CodeReader.read(code_dir + "code path"); - /* - * 3、trace - */ - map(classes_in_CD, java_files); - } } diff --git a/src/test/resources/attri b/src/test/resources/attri new file mode 100644 index 0000000000000000000000000000000000000000..b13c419086581dfdb008030902cd6c0fecc2b94f --- /dev/null +++ b/src/test/resources/attri @@ -0,0 +1,2 @@ ++host: String ++port: int \ No newline at end of file