diff --git a/src/main/java/com/hy/java/uct/cdtocode/CDToCodeTracer.java b/src/main/java/com/hy/java/uct/cdtocode/CDToCodeTracer.java index a6eb3f74f2a4dc479d54789dc9bcc22ad65f17dd..b9bae27a7cb03e1d9730416a6f23f36881b70ce6 100644 --- a/src/main/java/com/hy/java/uct/cdtocode/CDToCodeTracer.java +++ b/src/main/java/com/hy/java/uct/cdtocode/CDToCodeTracer.java @@ -3,6 +3,7 @@ package com.hy.java.uct.cdtocode; import java.util.Map; import com.hy.java.uct.cdtocode.mapper.InitialMapper; +import com.hy.java.uct.cdtocode.mapper.LevelMapper; import com.hy.java.uct.cdtocode.reader.CDReader; import com.hy.java.uct.cdtocode.reader.CodeReader; import com.hy.java.uct.cdtocode.reader.DocReader; @@ -33,7 +34,7 @@ public class CDToCodeTracer { */ // 读取完UML图识别结果后,将实体信息保存在classes_in_CD里 Map classes_in_CD = CDReader.read(cd_dir + "cd-eclipse_jetty.txt"); - // 检查读取结果,可注释掉 + // 检查结果,可注释掉 // CDReader.check(classes_in_CD); /* * 2、读取文档信息 @@ -43,7 +44,7 @@ public class CDToCodeTracer { * 3、读取code path指定的目录下所有java文件 */ Map java_files = CodeReader.read(code_dir + "code path"); - // 检查读取结果,可注释掉 + // 检查结果,可注释掉 // CodeReader.check(java_files); /* * 4、遍历模型中的实体元素(类或对象),针对每个元素,在code中寻找能匹配的java文件 @@ -52,6 +53,20 @@ public class CDToCodeTracer { * 词缀词典等语料库,定义基于类名称匹配和关联关系的启发式匹配规则,研究基于模糊匹配技术的追踪关系建立方法,基于初始追踪和启发式规则, * 对类名不一致的模型和代码元素进行启发式追踪,扩展初始追踪关系。 */ - InitialMapper.map(classes_in_CD, java_files, doc); + Map mapped_classes = InitialMapper.map(classes_in_CD, java_files, doc); + // 检查结果,可注释掉 + // InitialMapper.check(mapped_classes); + /* + * 5、基于类图语义增强和潜在语义分析的抽象概念与代码元素追踪方法 + * + * 参考ISO 12207、IEEE + * 1016等行业标准,定义包含类的名称、属性、方法和关系类型及指向的结构化文本语义模板,用于从详细设计中提取与模型内容相关的语义信息; + * 研究基于自然语言处理技术和语义模板、从文档中提取与模型元素相关信息的方法,增强模型中类和各种关系的信息量、细化其具体含义,提高其与代码追踪的可用程度; + * 针对语义增强的UML类图与代码,首先利用逆向工程技术建立代码模型,以支持代码与设计模型的追踪;然后基于潜在语义索引技术,分析设计模型中的类的名称、属性、 + * 方法、关系、功能描述等语义与代码类之间的语义关联,结合初始追踪关系中类之间的关系,建立抽象元素与代码之间的追踪 + */ + mapped_classes = LevelMapper.map(mapped_classes, java_files, doc); + // 检查结果,可注释掉 + LevelMapper.check(mapped_classes); } } diff --git a/src/main/java/com/hy/java/uct/cdtocode/mapper/InitialMapper.java b/src/main/java/com/hy/java/uct/cdtocode/mapper/InitialMapper.java index 088c7de8a020342347d8f415d045baf467e3d1ee..3c1d4aee2adb3b55cdcf5f4a40a070804806d068 100644 --- a/src/main/java/com/hy/java/uct/cdtocode/mapper/InitialMapper.java +++ b/src/main/java/com/hy/java/uct/cdtocode/mapper/InitialMapper.java @@ -1,14 +1,29 @@ 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.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.ImportDeclaration; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.PackageDeclaration; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.hy.java.uct.umlrecog.util.Relation; import com.hy.java.uct.umlrecog.util.UMLClass; +import com.hy.java.utility.common.Pair; public class InitialMapper { /** - * 针对类图中的类和代码中的类,基于同义词、词缀词典等语料于库,定义基类名称匹配和关联关系的启发式匹配规则,研究基于模糊匹配技术的追踪关系建立方法,解决类名不一致的模型和代码元素的追踪问题: + * 针对类图中的类和代码中的类,基于同义词、词缀词典等语料库,定义基类名称匹配和关联关系的启发式匹配规则,研究基于模糊匹配技术的追踪关系建立方法,解决类名不一致的模型和代码元素的追踪问题: *

*

    *
  • 1、针对类图中的每个类,基于类的名称、与代码中的类文件进行字符串模糊匹配,从源码文件中选出可能映射的文件,存在UML_class.possible_java_files中 @@ -21,8 +36,10 @@ public class InitialMapper { * @param classes_in_CD 键值对:<类名(图里的类名),类对象(UMLClass对象)> * @param java_files 键值对:<类全称(包+类名),java文件地址(可用javaparser解析文件内容)> * @param doc + * + * @return 根据名称相似度和关系启发式映射的classes */ - public static void map(Map classes_in_CD, Map java_files, String doc) { + public static Map map(Map classes_in_CD, Map java_files, String doc) { Set key_set = classes_in_CD.keySet(); // 1、针对类图中的每个类,基于类的名称、与代码中的类文件进行字符串模糊匹配,从源码文件中选出可能映射的文件,存在UML_class.possible_java_files中 for (String key : key_set) { @@ -35,26 +52,129 @@ public class InitialMapper { } } } - // 2、然后针对UML_class的每条out_rela,记录其target_class,看possible_java_files中的每个文件是否也有out_rela,且文件的out_rela目标文件也在target_class的possible_java_files中。如果有,则UML_class映射到该文件,且target_class映射到文件的out_rela目标文件。 + // 2、然后针对UML_class的每条out_rela,记录其target_class,看possible_java_files中的每个文件是否也有out_rela,且文件的out_rela目标文件也在target_class的possible_java_files中。如果有,则UML_class映射到该文件,且记录target_class和其映射文件。 for (String key : key_set) { UMLClass UML_class = classes_in_CD.get(key); if (UML_class.possible_java_files.size() < 1) { - System.out.println(UML_class.getTitle() + "\t" + "没有映射"); + // System.out.println(UML_class.getTitle() + "\t" + "没有映射"); } else { - for (String possible_java_file : UML_class.possible_java_files) { - // 针对UML_class的每条out_rela... - // 针对UML_class的每条out_rela... - // 针对UML_class的每条out_rela... - // 针对UML_class的每条out_rela... - // 针对UML_class的每条out_rela... - // 针对UML_class的每条out_rela... - // 针对UML_class的每条out_rela... - // 针对UML_class的每条out_rela... - System.out.println(UML_class.getTitle() + "\t" + possible_java_file); + // 如果该UML_class没有out_rela,则possible_java_files就是映射结果 + if (UML_class.out_relas.isEmpty()) { + for (String possible_java_full_name : UML_class.possible_java_files) { + UML_class.mapped_java_files.add(Pair.createPair(java_files.get(possible_java_full_name), null)); + } + } else { + // 针对UML_class的每条out_rela + for (Relation 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 + UML_class.mapped_java_files.add(Pair.createPair(java_files.get(possible_java_full_name), + Pair.createPair(Pair.createPair(target_class, java_files.get(if_target_class_possible_files_contain_class.getRight())), "继承"))); + // 目标类target_class正好也映射到一个文件上了。这会把一些只有进没有出的class也映射到 + target_class.mapped_java_files.add(Pair.createPair(java_files.get(if_target_class_possible_files_contain_class.getRight()), null)); + // 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 + UML_class.mapped_java_files.add(Pair.createPair(java_files.get(possible_java_full_name), + Pair.createPair(Pair.createPair(target_class, java_files.get(if_target_class_possible_files_contain_class.getRight())), "实现"))); + // 目标类target_class正好也映射到一个文件上了。这会把一些只有进没有出的class也映射到 + target_class.mapped_java_files.add(Pair.createPair(java_files.get(if_target_class_possible_files_contain_class.getRight()), null)); + // 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 + UML_class.mapped_java_files.add(Pair.createPair(java_files.get(possible_java_full_name), + Pair.createPair(Pair.createPair(target_class, java_files.get(if_target_class_possible_files_contain_class.getRight())), "依赖"))); + // 目标类target_class正好也映射到一个文件上了。这会把一些只有进没有出的class也映射到 + target_class.mapped_java_files.add(Pair.createPair(java_files.get(if_target_class_possible_files_contain_class.getRight()), null)); + // 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(); + } + } + } } } } - // 3、in_rela同上。 + // 3、in_rela同上。似乎不需要做in_rela,因为遍历完所有类的out_rela后,所有关系就都遍历了 + // 4、对classes_in_CD中的UMLClass.mapped_java_files去重。对其映射到的每个文件,如果该映射没有关系目标、且有重复,则去重 + Set mapped_classes_key_set = classes_in_CD.keySet(); + for (String mapped_classes_key : mapped_classes_key_set) { + UMLClass UML_class = classes_in_CD.get(mapped_classes_key); + Set mapped_file_no_target = new HashSet<>(); + List, String>>> mapped_file_has_target = new ArrayList<>(); + if (!UML_class.mapped_java_files.isEmpty()) { + for (Pair, String>> java_file_and_target_class : UML_class.mapped_java_files) { + if (java_file_and_target_class.getRight() == null) { + mapped_file_no_target.add(java_file_and_target_class.getLeft()); + } 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) { + UML_class.mapped_java_files.add(Pair.createPair(file_dir, null)); + } + UML_class.mapped_java_files.addAll(mapped_file_has_target); + } + } + return classes_in_CD; } /** @@ -68,20 +188,70 @@ public class InitialMapper { // 如果两个String相似,则返回true else { // 处理一下两个String,然后看它们是否相似 - - // 处理处理处理处理处理处理 - // 处理处理处理处理处理处理 - // 处理处理处理处理处理处理 - // 处理处理处理处理处理处理 - // 处理处理处理处理处理处理 - // 处理处理处理处理处理处理 - // 处理处理处理处理处理处理 + String processed_java_file_name = java_file_name.toLowerCase(); + String processed_UML_class_title = UML_class_title.toLowerCase(); // 判断处理后的两个String是否相似 - if (java_file_name.contains(UML_class_title)) { + if (processed_java_file_name.contains(processed_UML_class_title)) { return true; } } // 如果两个String既不相等也不相似,则返回false return false; } + + /** + * 判断文件的out_rela目标文件是否也在target_class的possible_java_files中 + */ + private static Pair possibleFilesContainClass(List possible_java_classes_full_name, String out_rela_target_class_full_name) { + 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)) { + result.setLeft(true); + result.setRight(possible_java_file_full_name); + break; + } + } + return result; + } + + private static String getClassFullName(String class_short_name, NodeList source_class_imports, Optional source_class_package_declaration) { + String result = class_short_name; + // 先默认class_short_name与source_class在同一个包下,所以先给它加上同样的包名。后面如果不在同包内,则直接连包名全改了 + if (source_class_package_declaration.isPresent()) { + result = source_class_package_declaration.get().getNameAsString() + "." + class_short_name; + } + // 再检查是否在imports中有class_short_name。如果有,则直接将result替换为imports。 + 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); + if (im_short_class_name.equals(class_short_name)) { + // 直接将result替换为imports + result = im_full_string; + break; + } + } + } + return result; + } + + 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 (Pair, String>> java_file_target_class : UML_class.mapped_java_files) { + if (java_file_target_class.getRight() != null) { + System.out.println(UML_class.getTitle() + "(映射为" + java_file_target_class.getLeft() + ")" + java_file_target_class.getRight().getRight() + "了" + + java_file_target_class.getRight().getLeft().getLeft().getTitle() + "(映射为" + java_file_target_class.getRight().getLeft().getRight() + ")"); + } else { + System.out.println(UML_class.getTitle() + "(映射为" + java_file_target_class.getLeft() + ")"); + } + } + } + } + } } diff --git a/src/main/java/com/hy/java/uct/cdtocode/mapper/LevelMapper.java b/src/main/java/com/hy/java/uct/cdtocode/mapper/LevelMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..a0164723453188c8301db0da4ea19feb59434a55 --- /dev/null +++ b/src/main/java/com/hy/java/uct/cdtocode/mapper/LevelMapper.java @@ -0,0 +1,42 @@ +package com.hy.java.uct.cdtocode.mapper; + +import java.util.Map; +import java.util.Set; + +import com.hy.java.uct.umlrecog.util.UMLClass; +import com.hy.java.utility.common.Pair; + +public class LevelMapper { + /** + * 参考ISO 12207、IEEE + * 1016等行业标准,定义包含类的名称、属性、方法和关系类型及指向的结构化文本语义模板,用于从详细设计中提取与模型内容相关的语义信息; + * 研究基于自然语言处理技术和语义模板、从文档中提取与模型元素相关信息的方法,增强模型中类和各种关系的信息量、细化其具体含义,提高其与代码追踪的可用程度; + * 针对语义增强的UML类图与代码,首先利用逆向工程技术建立代码模型,以支持代码与设计模型的追踪;然后基于潜在语义索引技术,分析设计模型中的类的名称、属性、 + * 方法、关系、功能描述等语义与代码类之间的语义关联,结合初始追踪关系中类之间的关系,建立抽象元素与代码之间的追踪 + */ + public static Map map(Map mapped_classes, Map java_files, String doc) { + // TODO Auto-generated method stub + return mapped_classes; + } + + 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 (Pair, String>> java_file_target_class : UML_class.mapped_java_files) { + if (java_file_target_class.getRight() != null) { + System.out.println(UML_class.getTitle() + "(映射为" + java_file_target_class.getLeft() + ")" + java_file_target_class.getRight().getRight() + "了" + + java_file_target_class.getRight().getLeft().getLeft().getTitle() + "(映射为" + java_file_target_class.getRight().getLeft().getRight() + ")"); + } else { + System.out.println(UML_class.getTitle() + "(映射为" + java_file_target_class.getLeft() + ")"); + } + } + } + } + } + +} diff --git a/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java b/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java index abea3328a200fdc07e60c89bd3983b12e65c6302..1100ac8b72c2c2a8415181343529b60185ba770f 100644 --- a/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java +++ b/src/main/java/com/hy/java/uct/umlrecog/util/UMLClass.java @@ -3,6 +3,8 @@ package com.hy.java.uct.umlrecog.util; import java.util.ArrayList; import java.util.List; +import com.hy.java.utility.common.Pair; + public class UMLClass { /** * 初始检测时用。后续不再使用 @@ -35,6 +37,13 @@ public class UMLClass { */ public List possible_java_files = new ArrayList<>(); + /** + * 真正的映射(由于抽象层次不同,所以可能不止一个java文件是真正的映射) + * + * 每个映射形式为,关系类型>> + */ + public List, String>>> mapped_java_files = new ArrayList<>(); + public String getTitle() { return title; }