sol2uml
Version:
Solidity contract visualisation tool.
177 lines • 8.14 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.findAssociatedClass = void 0;
// Find the UML class linked to the association
const findAssociatedClass = (association, sourceUmlClass, umlClasses, searchedAbsolutePaths = []) => {
const umlClass = umlClasses.find((targetUmlClass) => {
const targetParentClass = association.parentUmlClassName &&
targetUmlClass.parentId !== undefined
? umlClasses[targetUmlClass.parentId]
: undefined;
return isAssociated(association, sourceUmlClass, targetUmlClass, targetParentClass);
});
// If a link was found
if (umlClass)
return umlClass;
// // Could not find association so now need to recursively look at imports of imports
// // add to already recursively processed files to avoid getting stuck in circular imports
// searchedAbsolutePaths.push(sourceUmlClass.absolutePath)
// const importedType = findChainedImport(
// association,
// sourceUmlClass,
// umlClasses,
// searchedAbsolutePaths,
// )
// if (importedType) return importedType
// // Still could not find association so now need to recursively look for inherited types
// const inheritedType = findInheritedType(
// association,
// sourceUmlClass,
// umlClasses,
// )
// if (inheritedType) return inheritedType
return undefined;
};
exports.findAssociatedClass = findAssociatedClass;
// Tests if source class can be linked to the target class via an association
const isAssociated = (association, sourceUmlClass, targetUmlClass, targetParentUmlClass) => {
if (association.parentUmlClassName) {
return (
// class is in the same source file
(association.targetUmlClassName === targetUmlClass.name &&
association.parentUmlClassName === targetParentUmlClass?.name &&
sourceUmlClass.absolutePath === targetUmlClass.absolutePath) ||
// imported classes with no explicit import names
(association.targetUmlClassName === targetUmlClass.name &&
association.parentUmlClassName === targetParentUmlClass?.name &&
sourceUmlClass.imports.some((i) => i.absolutePath === targetUmlClass.absolutePath &&
i.classNames.length === 0)) ||
// imported classes with explicit import names or import aliases
sourceUmlClass.imports.some((importLink) => importLink.absolutePath === targetUmlClass.absolutePath &&
importLink.classNames.some((importedClass) =>
// If a parent contract with no import alias
(association.targetUmlClassName ===
targetUmlClass.name &&
association.parentUmlClassName ===
importedClass.className &&
importedClass.alias == undefined) ||
// If a parent contract with import alias
(association.targetUmlClassName ===
targetUmlClass.name &&
association.parentUmlClassName ===
importedClass.alias))));
}
// No parent class in the association
return (
// class is in the same source file
(association.targetUmlClassName === targetUmlClass.name &&
sourceUmlClass.absolutePath === targetUmlClass.absolutePath) ||
// imported classes with no explicit import names
(association.targetUmlClassName === targetUmlClass.name &&
sourceUmlClass.imports.some((i) => i.absolutePath === targetUmlClass.absolutePath &&
i.classNames.length === 0)) ||
// imported classes with explicit import names or import aliases
sourceUmlClass.imports.some((importLink) => importLink.absolutePath === targetUmlClass.absolutePath &&
importLink.classNames.some((importedClass) =>
// no import alias
(association.targetUmlClassName ===
importedClass.className &&
importedClass.className === targetUmlClass.name &&
importedClass.alias == undefined) ||
// import alias
(association.targetUmlClassName ===
importedClass.alias &&
importedClass.className === targetUmlClass.name))));
};
const findInheritedType = (association, sourceUmlClass, umlClasses) => {
// Get all realized associations.
const parentAssociations = sourceUmlClass.getParentContracts();
// For each parent association
for (const parentAssociation of parentAssociations) {
const parent = (0, exports.findAssociatedClass)(parentAssociation, sourceUmlClass, umlClasses);
if (!parent)
continue;
// For each struct on the parent
for (const structId of parent.structs) {
const structUmlClass = umlClasses.find((c) => c.id === structId);
if (!structUmlClass)
continue;
if (structUmlClass.name === association.targetUmlClassName) {
return structUmlClass;
}
}
// For each enum on the parent
for (const enumId of parent.enums) {
const enumUmlClass = umlClasses.find((c) => c.id === enumId);
if (!enumUmlClass)
continue;
if (enumUmlClass.name === association.targetUmlClassName) {
return enumUmlClass;
}
}
// Recursively look for inherited types
const targetClass = findInheritedType(association, parent, umlClasses);
if (targetClass)
return targetClass;
}
return undefined;
};
// const findChainedImport = (
// association: Association,
// sourceUmlClass: UmlClass,
// umlClasses: readonly UmlClass[],
// searchedRelativePaths: string[],
// ): UmlClass | undefined => {
// // Get all valid imports. That is, imports that do not explicitly import contracts or interfaces
// // or explicitly import the source class
// const imports = sourceUmlClass.imports.filter(
// (i) =>
// i.classNames.length === 0 ||
// i.classNames.some(
// (cn) =>
// (association.targetUmlClassName === cn.className &&
// !cn.alias) ||
// association.targetUmlClassName === cn.alias,
// ),
// )
// // For each import
// for (const importDetail of imports) {
// // Find a class with the same absolute path as the import so we can get the new imports
// const newSourceUmlClass = umlClasses.find(
// (c) => c.absolutePath === importDetail.absolutePath,
// )
// if (!newSourceUmlClass) {
// // Could not find a class in the import file so just move onto the next loop
// continue
// }
// // Avoid circular imports
// if (searchedRelativePaths.includes(newSourceUmlClass.absolutePath)) {
// // Have already recursively looked for imports of imports in this file
// continue
// }
//
// // find class linked to the association without aliased imports
// const umlClass = findAssociatedClass(
// association,
// newSourceUmlClass,
// umlClasses,
// searchedRelativePaths,
// )
// if (umlClass) return umlClass
//
// // find all aliased imports
// const aliasedImports = importDetail.classNames.filter((cn) => cn.alias)
// // For each aliased import
// for (const aliasedImport of aliasedImports) {
// const umlClass = findAssociatedClass(
// { ...association, targetUmlClassName: aliasedImport.className },
// newSourceUmlClass,
// umlClasses,
// searchedRelativePaths,
// )
// if (umlClass) return umlClass
// }
// }
// return undefined
// }
//# sourceMappingURL=associations.js.map