Пример #1
0
 def _visit(self, node: Any) -> Optional[Any]:
     if isinstance(node, (YAMLRoot, dict)) or is_YAMLROOT(node):
         if isinstance(node, YAMLRoot) or is_YAMLROOT(node):
             node = self._add_type(node)
         for k, v in list(node.items()):
             if v:
                 new_v = self._visit(v)
                 if new_v is not None:
                     node[k] = new_v
     elif isinstance(node, list):
         for i in range(0, len(node)):
             new_v = self._visit(node[i])
             if new_v is not None:
                 node[i] = new_v
     elif isinstance(node, set):
         for v in list(node):
             new_v = self._visit(v)
             if new_v is not None:
                 node.remove(v)
                 node.add(new_v)
     elif isinstance(node, ClassDefinitionName):
         return ClassDefinitionName(camelcase(node))
     elif isinstance(node, SlotDefinitionName):
         return SlotDefinitionName(underscore(node))
     elif isinstance(node, TypeDefinitionName):
         return TypeDefinitionName(underscore(node))
     elif isinstance(node, SubsetDefinitionName):
         return SubsetDefinitionName(underscore(node))
     elif isinstance(node, ElementName):
         return ClassDefinitionName(camelcase(node)) if node in self.schema.classes else \
             SlotDefinitionName(underscore(node)) if node in self.schema.slots else \
             SubsetDefinitionName(camelcase(node)) if node in self.schema.subsets else \
             TypeDefinitionName(underscore(node)) if node in self.schema.types else None
     return None
Пример #2
0
    def visit_schema(self,
                     classes: Set[ClassDefinitionName] = None,
                     directory: Optional[str] = None,
                     load_image: bool = True,
                     **_) -> None:
        if directory:
            os.makedirs(directory, exist_ok=True)
        if classes is not None:
            for cls in classes:
                if cls not in self.schema.classes:
                    raise ValueError(f"Unknown class name: {cls}")
        self.box_generated = set()
        self.associations_generated = set()
        self.focus_classes = classes
        if classes:
            self.gen_classes = self.neighborhood(
                list(classes)).classrefs.union(classes)
        else:
            self.gen_classes = self.synopsis.roots.classrefs
        self.referenced = self.gen_classes
        self.generated = set()
        yumlclassdef: List[str] = []
        while self.referenced.difference(self.generated):
            cn = sorted(list(self.referenced.difference(self.generated)),
                        reverse=True)[0]
            self.generated.add(cn)
            assocs = self.class_associations(ClassDefinitionName(cn), cn
                                             in self.referenced)
            if assocs:
                yumlclassdef.append(assocs)
            else:
                yumlclassdef.append(self.class_box(ClassDefinitionName(cn)))

        yuml_url = str(YUML) + ','.join(yumlclassdef) + \
                   (('.' + self.format) if self.format not in ('yuml', 'svg') else '')
        file_suffix = '.svg' if self.format == 'yuml' else '.' + self.format
        if directory:
            self.output_file_name = os.path.join(
                directory,
                camelcase(sorted(classes)[0] if classes else self.schema.name)
                + file_suffix)
            if load_image:
                resp = requests.get(yuml_url, stream=True)
                if resp.ok:
                    with open(self.output_file_name, 'wb') as f:
                        for chunk in resp.iter_content(chunk_size=2048):
                            f.write(chunk)
                else:
                    self.logger.error(f"{resp.reason} accessing {yuml_url}")
        else:
            print(str(YUML) + ','.join(yumlclassdef), end='')
Пример #3
0
    def add_ref(
        self,
        fromtype: RefType,
        fromname: ElementName,
        totype: RefType,
        toname: ElementName,
    ) -> None:
        """ Add an inverse reference, indicating that to type/name is referenced by from type/name

        :param fromtype: Referencer type
        :param fromname: Referencer name
        :param totype: Referencee type
        :param toname: Referencee name
        :return:
        """
        if totype is ClassType:
            self.classrefs.setdefault(ClassDefinitionName(toname),
                                      References()).addref(fromtype, fromname)
        elif totype is SlotType:
            self.slotrefs.setdefault(SlotDefinitionName(toname),
                                     References()).addref(fromtype, fromname)
        elif totype is TypeType:
            self.typerefs.setdefault(TypeDefinitionName(toname),
                                     References()).addref(fromtype, fromname)
        elif totype is SubsetType:
            self.subsetrefs.setdefault(SubsetDefinitionName(toname),
                                       References()).addref(
                                           fromtype, fromname)
        elif totype is EnumType:
            self.enumrefs.setdefault(SlotDefinitionName(toname),
                                     References()).addref(fromtype, fromname)
        else:
            raise TypeError("Unknown typ: {typ}")
Пример #4
0
    def class_identifier_path(self, cls_or_clsname: Union[str,
                                                          ClassDefinition],
                              force_non_key: bool) -> List[str]:
        """
        Return the path closure to a class identifier if the class has a key and force_non_key is false otherwise
        return a dictionary closure.

        :param cls_or_clsname: class definition
        :param force_non_key: True means inlined even if the class has a key
        :return: path
        """
        cls = cls_or_clsname if isinstance(cls_or_clsname, ClassDefinition) \
            else self.schema.classes[ClassDefinitionName(cls_or_clsname)]

        # Determine whether the class has a key
        identifier_slot = None
        if not force_non_key:
            identifier_slot = self.class_identifier(cls)

        # No key or inlined, its closure is a dictionary
        if identifier_slot is None:
            return ['dict', self.class_or_type_name(cls.name)]

        # We're dealing with a reference
        pathname = camelcase(cls.name + ' ' +
                             self.aliased_slot_name(identifier_slot))
        if cls.is_a:
            parent_identifier_slot = self.class_identifier(cls.is_a)
            if parent_identifier_slot:
                return self.class_identifier_path(cls.is_a, False) + [pathname]
        return self.slot_range_path(identifier_slot) + [pathname]
Пример #5
0
 def _range_uri(self, slot: SlotDefinition) -> URIRef:
     if slot.range in self.schema.types:
         return self._type_uri(TypeDefinitionName(slot.range))
     elif slot.range in self.schema.enums:
         # TODO: enums fill this in
         return self._enum_uri(EnumDefinitionName(slot.range))
     else:
         return self._class_uri(ClassDefinitionName(slot.range))
Пример #6
0
 def adjust_slot(self, slot: SlotDefinition) -> None:
     if slot.range in self.schema.classes:
         slot.range = ClassDefinitionName(camelcase(slot.range))
     elif slot.range in self.schema.slots:
         slot.range = SlotDefinitionName(underscore(slot.range))
     elif slot.range in self.schema.types:
         slot.range = TypeDefinitionName(underscore(slot.range))
     slot.slot_uri = self.namespaces.uri_for(slot.slot_uri)
Пример #7
0
 def class_or_type_for(self, name: str) -> Optional[Element]:
     """
     Return the corresponding class or type for name
     """
     if name in self.schema.classes:
         return self.schema.classes[ClassDefinitionName(name)]
     elif name in self.schema.types:
         return self.schema.types[TypeDefinitionName(name)]
     elif name in self.schema.enums:
         return self.schema.enums[EnumDefinitionName(name)]
     return None
Пример #8
0
 def addref(self, fromtype: RefType, fromname: ElementName) -> None:
     if fromtype is ClassType:
         self.classrefs.add(ClassDefinitionName(fromname))
     elif fromtype is TypeType:
         self.typerefs.add(TypeDefinitionName(fromname))
     elif fromtype is SlotType:
         self.slotrefs.add(SlotDefinitionName(fromname))
     elif fromtype is SubsetType:
         self.subsetrefs.add(SubsetDefinitionName(fromname))
     elif fromtype is EnumType:
         self.slotrefs.add(EnumDefinitionName(fromname))
     else:
         raise TypeError(f"Unknown typ: {fromtype}")
Пример #9
0
    def class_associations(self,
                           cn: ClassDefinitionName,
                           must_render: bool = False) -> str:
        """ Emit all associations for a focus class.  If none are specified, all classes are generated

        @param cn: Name of class to be emitted
        @param must_render: True means render even if this is a target (class is specifically requested)
        @return: YUML representation of the association
        """

        # NOTE: YUML diagrams draw in the opposite order in which they are created, so we work from bottom to top and
        # from right to left
        assocs: List[str] = []
        if cn not in self.associations_generated and (
                not self.focus_classes or cn in self.focus_classes):
            cls = self.schema.classes[cn]

            # Slots that reference other classes
            for slot in self.filtered_cls_slots(
                    cn, False, lambda s: s.range in self.schema.classes)[::-1]:
                # Swap the two boxes because, in the case of self reference, the last definition wins
                if not slot.range in self.associations_generated and cn in slot.domain_of:
                    rhs = self.class_box(cn)
                    lhs = self.class_box(cast(ClassDefinitionName, slot.range))
                    assocs.append(
                        lhs + '<' + self.aliased_slot_name(slot) +
                        self.prop_modifier(cls, slot) +
                        self.cardinality(slot, False) +
                        (yuml_inline_rev if slot.inlined else yuml_ref) + rhs)

            # Slots in other classes that reference this
            for slotname in sorted(self.synopsis.rangerefs.get(cn, [])):
                slot = self.schema.slots[slotname]
                # Don't do self references twice
                # Also, slot must be owned by the class
                if cls.name not in slot.domain_of and cls.name not in self.associations_generated:
                    for dom in [
                            self.schema.classes[dof] for dof in slot.domain_of
                    ]:
                        assocs.append(
                            self.class_box(dom.name) +
                            (yuml_inline if slot.inlined else yuml_ref) +
                            self.aliased_slot_name(slot) +
                            self.prop_modifier(dom, slot) +
                            self.cardinality(slot, False) + '>' +
                            self.class_box(cn))

            # Mixins used in the class
            for mixin in cls.mixins:
                assocs.append(
                    self.class_box(cn) + yuml_uses + self.class_box(mixin))

            # Classes that use the class as a mixin
            if cls.name in self.synopsis.mixinrefs:
                for mixin in sorted(
                        self.synopsis.mixinrefs[cls.name].classrefs,
                        reverse=True):
                    assocs.append(
                        self.class_box(ClassDefinitionName(mixin)) +
                        yuml_uses + self.class_box(cn))

            # Classes that inject information
            if cn in self.synopsis.applytos.classrefs:
                for injector in sorted(self.synopsis.applytorefs[cn].classrefs,
                                       reverse=True):
                    assocs.append(
                        self.class_box(cn) + yuml_injected +
                        self.class_box(ClassDefinitionName(injector)))
            self.associations_generated.add(cn)

            # Children
            if cn in self.synopsis.isarefs:
                for is_a_cls in sorted(self.synopsis.isarefs[cn].classrefs,
                                       reverse=True):
                    assocs.append(
                        self.class_box(cn) + yuml_is_a +
                        self.class_box(ClassDefinitionName(is_a_cls)))

            # Parent
            if cls.is_a and cls.is_a not in self.associations_generated:
                assocs.append(
                    self.class_box(cls.is_a) + yuml_is_a + self.class_box(cn))
        return ','.join(assocs)