annatomic 0.4.0

The Annatomic annotation editor is intended to be used for the [RIDGES corpus](https://www.linguistik.hu-berlin.de/en/institut-en/professuren-en/korpuslinguistik/research/ridges-projekt). It is based on [graphANNIS](https://github.com/korpling/graphANNIS) and thus is internal data model is in principle suitable for a wide range of annotation concepts. "
Documentation
use graphannis::model::AnnotationComponentType;
use graphannis_core::graph::{
    ANNIS_NS,
    update::{GraphUpdate, UpdateEvent},
};

/// Create update events for the following corpus structure:
///
/// ```
///     root
///       |
///      docc1
/// ```
pub fn create_corpus_structure_simple(update: &mut GraphUpdate) {
    update
        .add_event(UpdateEvent::AddNode {
            node_name: "root".to_string(),
            node_type: "corpus".to_string(),
        })
        .unwrap();

    update
        .add_event(UpdateEvent::AddNode {
            node_name: "root/doc1".to_string(),
            node_type: "corpus".to_string(),
        })
        .unwrap();

    update
        .add_event(UpdateEvent::AddNodeLabel {
            node_name: "root/doc1".to_string(),
            anno_ns: ANNIS_NS.into(),
            anno_name: "doc".into(),
            anno_value: "doc1".into(),
        })
        .unwrap();

    update
        .add_event(UpdateEvent::AddEdge {
            source_node: "root/doc1".to_string(),
            target_node: "root".to_string(),
            layer: ANNIS_NS.to_string(),
            component_type: "PartOf".to_string(),
            component_name: "".to_string(),
        })
        .unwrap();

    update
        .add_event(UpdateEvent::AddNode {
            node_name: "root/doc1#text1".to_string(),
            node_type: "datasource".to_string(),
        })
        .unwrap();

    update
        .add_event(UpdateEvent::AddEdge {
            source_node: "root/doc1#text1".to_string(),
            target_node: "root/doc1".to_string(),
            layer: ANNIS_NS.to_string(),
            component_type: "PartOf".to_string(),
            component_name: "".to_string(),
        })
        .unwrap();
}

/// Creates éxample token objects. If a document name is given, the
/// token objects are attached to it.
///
/// The example tokens are
/// - Is (`tok0`)
/// - this (`tok1`)
/// - example (`tok2`)
/// - more (`tok3`)
/// - complicated (`tok4`)
/// - than (`tok5`)
/// - it (`tok6`)
/// - appears (`tok7`)
/// - to (`tok8`)
/// - be (`tok9`)
/// - ? (`tok10`)
///
pub fn create_tokens(update: &mut GraphUpdate, document_node: Option<&str>) {
    let prefix = if let Some(document_node) = document_node {
        format!("{}#", document_node)
    } else {
        "".to_string()
    };

    let token_strings = [
        ("Is", " "),
        ("this", " "),
        ("example", " "),
        ("more", " "),
        ("complicated", " "),
        ("than", " "),
        ("it", " "),
        ("appears", " "),
        ("to", " "),
        ("be", ""),
        ("?", ""),
    ];
    for (i, (t, ws)) in token_strings.iter().enumerate() {
        let ws = if ws.is_empty() { None } else { Some(*ws) };
        create_token_node(
            update,
            &format!("{}tok{}", prefix, i),
            t,
            None,
            ws,
            document_node,
        );
    }

    // add the order relations
    for i in 0..token_strings.len() {
        update
            .add_event(UpdateEvent::AddEdge {
                source_node: format!("{}tok{}", prefix, i),
                target_node: format!("{}tok{}", prefix, i + 1),
                layer: ANNIS_NS.to_string(),
                component_type: "Ordering".to_string(),
                component_name: "".to_string(),
            })
            .unwrap();
    }
}

/// Add an additional segmentation layer to the tokens created by `create_tokens()`.
pub fn create_segmentation(update: &mut GraphUpdate) {
    make_span(
        update,
        "root/doc1#seg1",
        &["root/doc1#tok1", "root/doc1#tok2", "root/doc1#tok3"],
        true,
    );
    update
        .add_event(UpdateEvent::AddNodeLabel {
            node_name: "root/doc1#seg1".into(),
            anno_ns: ANNIS_NS.into(),
            anno_name: "tok".into(),
            anno_value: "This".into(),
        })
        .unwrap();
    update
        .add_event(UpdateEvent::AddEdge {
            source_node: "root/doc1#seg1".into(),
            target_node: "root/doc1".into(),
            layer: ANNIS_NS.into(),
            component_type: AnnotationComponentType::PartOf.to_string(),
            component_name: "".into(),
        })
        .unwrap();

    make_span(update, "root/doc1#seg2", &["root/doc1#tok4"], true);
    update
        .add_event(UpdateEvent::AddNodeLabel {
            node_name: "root/doc1#seg2".into(),
            anno_ns: ANNIS_NS.into(),
            anno_name: "tok".into(),
            anno_value: "more".into(),
        })
        .unwrap();
    update
        .add_event(UpdateEvent::AddEdge {
            source_node: "root/doc1#seg2".into(),
            target_node: "root/doc1".into(),
            layer: ANNIS_NS.into(),
            component_type: AnnotationComponentType::PartOf.to_string(),
            component_name: "".into(),
        })
        .unwrap();

    make_span(update, "root/doc1#seg3", &["root/doc1#tok5"], true);
    update
        .add_event(UpdateEvent::AddNodeLabel {
            node_name: "root/doc1#seg3".into(),
            anno_ns: ANNIS_NS.into(),
            anno_name: "tok".into(),
            anno_value: "complicated".into(),
        })
        .unwrap();
    update
        .add_event(UpdateEvent::AddEdge {
            source_node: "root/doc1#seg3".into(),
            target_node: "root/doc1".into(),
            layer: ANNIS_NS.into(),
            component_type: AnnotationComponentType::PartOf.to_string(),
            component_name: "".into(),
        })
        .unwrap();

    // add the order relations for the segmentation
    update
        .add_event(UpdateEvent::AddEdge {
            source_node: "root/doc1#seg1".into(),
            target_node: "root/doc1#seg2".into(),
            layer: ANNIS_NS.to_string(),
            component_type: "Ordering".to_string(),
            component_name: "seg".to_string(),
        })
        .unwrap();
    update
        .add_event(UpdateEvent::AddEdge {
            source_node: "root/doc1#seg2".into(),
            target_node: "root/doc1#seg3".into(),
            layer: ANNIS_NS.to_string(),
            component_type: "Ordering".to_string(),
            component_name: "seg".to_string(),
        })
        .unwrap();
}

pub fn create_token_node(
    update: &mut GraphUpdate,
    node_name: &str,
    token_value: &str,
    whitespace_before: Option<&str>,
    whitespace_after: Option<&str>,
    document_node: Option<&str>,
) {
    update
        .add_event(UpdateEvent::AddNode {
            node_name: node_name.to_string(),
            node_type: "node".to_string(),
        })
        .unwrap();
    update
        .add_event(UpdateEvent::AddNodeLabel {
            node_name: node_name.to_string(),
            anno_ns: ANNIS_NS.to_string(),
            anno_name: "tok".to_string(),
            anno_value: token_value.to_string(),
        })
        .unwrap();

    if let Some(ws) = whitespace_before {
        update
            .add_event(UpdateEvent::AddNodeLabel {
                node_name: node_name.to_string(),
                anno_ns: ANNIS_NS.to_string(),
                anno_name: "tok-whitespace-before".to_string(),
                anno_value: ws.to_string(),
            })
            .unwrap();
    }
    if let Some(ws) = whitespace_after {
        update
            .add_event(UpdateEvent::AddNodeLabel {
                node_name: node_name.to_string(),
                anno_ns: ANNIS_NS.to_string(),
                anno_name: "tok-whitespace-after".to_string(),
                anno_value: ws.to_string(),
            })
            .unwrap();
    }

    if let Some(parent_node) = document_node {
        // add the token node to the document
        update
            .add_event(UpdateEvent::AddEdge {
                source_node: node_name.to_string(),
                target_node: parent_node.to_string(),
                layer: ANNIS_NS.to_string(),
                component_type: "PartOf".to_string(),
                component_name: "".to_string(),
            })
            .unwrap();
    }
}

pub fn make_span(
    update: &mut GraphUpdate,
    node_name: &str,
    covered_token_names: &[&str],
    create_source: bool,
) {
    if create_source {
        update
            .add_event(UpdateEvent::AddNode {
                node_name: node_name.to_string(),
                node_type: "node".to_string(),
            })
            .unwrap();
    }
    for c in covered_token_names {
        update
            .add_event(UpdateEvent::AddEdge {
                source_node: node_name.to_string(),
                target_node: c.to_string(),
                layer: "".to_string(),
                component_type: "Coverage".to_string(),
                component_name: "".to_string(),
            })
            .unwrap();
    }
}