# Source code for cantools.database.diagnostics.database

    import logging
    from typing import TextIO

    from cantools.database.diagnostics.did import Did
    from cantools.typechecking import StringPathLike

    from .formats import cdd

    LOGGER = logging.getLogger(__name__)

    class Database:
        """This class contains all DIDs.

        The factory functions :func:`load()<cantools.database.load()>`,
        :func:`load_file()<cantools.database.load_file()>` and
        :func:`load_string()<cantools.database.load_string()>` returns
        instances of this class.

        """

        def __init__(self, dids: list[Did] | None = None):
            self._name_to_did: dict[str, Did] = {}
            self._identifier_to_did: dict[int, Did] = {}
            self._dids = dids or []
            self.refresh()

        @property
        def dids(self) -> list[Did] | None:
            """A list of DIDs in the database.

            """

            return self._dids

        def add_cdd(self, fp: TextIO) -> None:
            """Read and parse CDD data from given file-like object and add the
            parsed data to the database.

            """

            self.add_cdd_string(fp.read())

        def add_cdd_file(self, filename: StringPathLike, encoding: str = 'utf-8') -> None:
            """Open, read and parse CDD data from given file and add the parsed
            data to the database.

            `encoding` specifies the file encoding.

            """

            with open(filename, encoding=encoding, errors='replace') as fin:
                self.add_cdd(fin)

        def add_cdd_string(self, string: str) -> None:
            """Parse given CDD data string and add the parsed data to the
            database.

            """

            database = cdd.load_string(string)
            self._dids = database.dids
            self.refresh()

        def _add_did(self, did: Did) -> None:
            """Add given DID to the database.

            """

            if did.name in self._name_to_did:
                LOGGER.warning("Overwriting DID with name '%s' in the "
                               "name to DID dictionary.",
                               did.name)

            if did.identifier in self._identifier_to_did:
                LOGGER.warning(
                    "Overwriting DID '%s' with '%s' in the identifier to DID "
                    "dictionary because they have identical identifiers 0x%x.",
                    self._identifier_to_did[did.identifier].name,
                    did.name,
                    did.identifier)

            self._name_to_did[did.name] = did
            self._identifier_to_did[did.identifier] = did

        def get_did_by_name(self, name: str) -> Did:
            """Find the DID object for given name `name`.

            """

            return self._name_to_did[name]

        def get_did_by_identifier(self, identifier: int) -> Did:
            """Find the DID object for given identifier `identifier`.

            """

            return self._identifier_to_did[identifier]

        def refresh(self) -> None:
            """Refresh the internal database state.

            This method must be called after modifying any DIDs in the
            database to refresh the internal lookup tables used when
            encoding and decoding DIDs.

            """

            self._name_to_did = {}
            self._identifier_to_did = {}

            for did in self._dids:
                did.refresh()
                self._add_did(did)

        def __repr__(self) -> str:
            lines: list[str] = []

            for did in self._dids:
                lines.append(repr(did))

                for data in did.datas:
                    lines.append('  ' + repr(data))

                lines.append('')

            return '\n'.join(lines)