def annotations(obj: BioCCollection or BioCDocument or BioCPassage or BioCSentence,
                docid: str = None, level: int = PASSAGE) -> Generator[BioCAnnotation, None, None]:
    """
    Get all annotations in document id.

    Args:
        obj: BioCCollection, BioCDocument, BioCPassage, or BioCSentence
        docid: document id. If None, all documents
        level: one of DOCUMENT, PASSAGE, SENTENCE

    Yields:
        one annotation
    """
    if isinstance(obj, BioCCollection):
        for document in filter(lambda d: docid is None or docid == d.id, obj.documents):
            yield from annotations(document, level=level)
    elif isinstance(obj, BioCDocument):
        if level == DOCUMENT:
            yield from obj.annotations
        elif level in (PASSAGE, SENTENCE):
            for passage in obj.passages:
                yield from annotations(passage, level=level)
        else:
            raise ValueError('level must be DOCUMENT, PASSAGE, or SENTENCE')
    elif isinstance(obj, BioCPassage):
        if level == PASSAGE:
            yield from obj.annotations
        elif level == SENTENCE:
            for sentence in obj.sentences:
                yield from annotations(sentence, level=level)
        else:
            raise ValueError('level must be PASSAGE or SENTENCE')
    elif isinstance(obj, BioCSentence):
        if level == SENTENCE:
            yield from obj.annotations
        else:
            raise ValueError('level must be SENTENCE')
    else:
        raise TypeError(f'Object of type {obj.__class__.__name__} must be BioCCollection, '
                        f'BioCDocument, BioCPassage, or BioCSentence')