All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.document.annotation.Annotation Maven / Gradle / Ivy

// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document.annotation;

import com.yahoo.document.DataType;
import com.yahoo.document.datatypes.FieldValue;

/**
 * An Annotation describes some kind of information associated with a {@link SpanNode}.
 *
 * @see com.yahoo.document.annotation.SpanNode
 * @see com.yahoo.document.annotation.AnnotationType
 * @author Einar M R Rosenvinge
 */
public class Annotation implements Comparable {

    private AnnotationType type;
    private SpanNode spanNode = null;
    private FieldValue value = null;

    /** This scratch id is used to avoid using IdentityHashMaps as they are very costly. */
    private int scratchId = -1;

    public void setScratchId(int id) {
        scratchId = id;
    }

    public int getScratchId() {
        return scratchId;
    }


    /**
     * Constructs an Annotation without a type. BEWARE! Should only be used during deserialization.
     */
    public Annotation() {
    }

    /**
     * Constructs a new annotation of the specified type.
     *
     * @param type the type of the new annotation
     */
    public Annotation(AnnotationType type) {
        this.type = type;
    }

    /**
     * Constructs a copy of the input annotation.
     *
     * @param other annotation
     */
    public Annotation(Annotation other) {
        this.type = other.type;
        this.value = ((other.value == null) ? null : other.value.clone());
        //do not copy spanNode now
    }

    /**
     * Constructs a new annotation of the specified type, and having the specified value.
     *
     * @param type  the type of the new annotation
     * @param value the value of the new annotation
     * @throws UnsupportedOperationException if the annotation type does not allow this annotation to have values.
     */
    public Annotation(AnnotationType type, FieldValue value) {
        this(type);
        setFieldValue(value);
    }

    /**
     * Returns the type of this annotation.
     *
     * @return the type of this annotation
     */
    public AnnotationType getType() {
        return type;
    }

    /**
     * Sets the type of this annotation. BEWARE! Should only be used during deserialization.
     *
     * @param type the type of this annotation
     */
    public void setType(AnnotationType type) {
        this.type = type;
    }

    /**
     * Returns true if this Annotation is associated with a SpanNode (irrespective of the SpanNode being valid or not).
     *
     * @return true if this Annotation is associated with a SpanNode, false otherwise.
     * @see com.yahoo.document.annotation.SpanNode#isValid()
     */
    public boolean hasSpanNode() {
        return spanNode != null;
    }

    /**
     * Returns true iff this Annotation is associated with a SpanNode and the SpanNode is valid.
     *
     * @return true iff this Annotation is associated with a SpanNode and the SpanNode is valid.
     * @see com.yahoo.document.annotation.SpanNode#isValid()
     */
    public boolean isSpanNodeValid() {
        return spanNode != null && spanNode.isValid();
    }


    /**
     * Returns the {@link SpanNode} this Annotation is annotating, if any.
     *
     * @return the {@link SpanNode} this Annotation is annotating, or null
     * @throws IllegalStateException if this Annotation is associated with a SpanNode and the SpanNode is invalid.
     */
    public SpanNode getSpanNode() {
        if (spanNode != null && !spanNode.isValid()) {
            throw new IllegalStateException("Span node is invalid: " + spanNode);
        }
        return spanNode;
    }

    /**
     * Returns the {@link SpanNode} this Annotation is annotating, if any.
     *
     * @return the {@link SpanNode} this Annotation is annotating, or null
     */
    public final SpanNode getSpanNodeFast() {
        return spanNode;
    }

    /**
     * WARNING! Should only be used by deserializers! Sets the span node that this annotation points to.
     *
     * @param spanNode the span node that this annotation shall point to.
     */
    public void setSpanNode(SpanNode spanNode) {
        if (this.spanNode != null && spanNode != null) {
            throw new IllegalStateException("WARNING! " + this + " is already attached to node " + this.spanNode
                                            + ". Attempt to attach to node " + spanNode
                                            + ". Annotation instances MUST NOT be shared among SpanNodes.");
        }
        if (spanNode != null && !spanNode.isValid()) {
            throw new IllegalStateException("Span node is invalid: " + spanNode);
        }
        if (spanNode == DummySpanNode.INSTANCE) {
            // internal safeguard
            throw new IllegalStateException("BUG! Annotations should never be attached to DummySpanNode.");
        }
        this.spanNode = spanNode;
    }

    /**
     * WARNING! Should only be used by deserializers! Sets the span node that this annotation points to.
     *
     * @param spanNode the span node that this annotation shall point to.
     */
    public final void setSpanNodeFast(SpanNode spanNode) {
        this.spanNode = spanNode;
    }

    /**
     * Returns the value of the annotation, if any.
     *
     * @return the value of the annotation, or null
     */
    public FieldValue getFieldValue() {
        return value;
    }

    /**
     * Sets the value of the annotation.
     *
     * @param fieldValue the value to set
     * @throws UnsupportedOperationException if the annotation type does not allow this annotation to have values.
     */
    public void setFieldValue(FieldValue fieldValue) {
        if (fieldValue == null) {
            value = null;
            return;
        }

        DataType type = getType().getDataType();
        if (type != null && type.isValueCompatible(fieldValue)) {
            this.value = fieldValue;
        } else {
            String typeName = (type == null) ? "null" : type.getValueClass().getName();
            throw new IllegalArgumentException("Argument is of wrong type, must be of type " + typeName
                                               + ", was " + fieldValue.getClass().getName());
        }
    }

    /**
     * Returns true if this Annotation has a value.
     *
     * @return true if this Annotation has a value, false otherwise.
     */
    public boolean hasFieldValue() {
        return value != null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Annotation)) return false;

        Annotation that = (Annotation) o;
        if (!type.equals(that.type)) return false;
        if (spanNode != null ? !spanNode.equals(that.spanNode) : that.spanNode != null) return false;
        if (value != null ? !value.equals(that.value) : that.value != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = type.hashCode();
        result = 31 * result + (spanNode != null ? spanNode.hashCode() : 0);
        result = 31 * result + (value != null ? value.toString().hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        String retval = "annotation of type " + type;
        retval += ((value == null) ? " (no value)" : " (with value)");
        retval += ((spanNode == null) ? " (no span)" : (" with span "+spanNode));
        return retval;
    }


    @Override
    public int compareTo(Annotation annotation) {
        int comp;

        if (spanNode == null) {
            comp = (annotation.spanNode == null) ? 0 : -1;
        } else {
            comp = (annotation.spanNode == null) ? 1 : spanNode.compareTo(annotation.spanNode);
        }

        if (comp != 0) {
            return comp;
        }

        comp = type.compareTo(annotation.type);

        if (comp != 0) {
            return comp;
        }

        // types are equal, too, compare values
        if (value == null) {
            comp = (annotation.value == null) ? 0 : -1;
        } else {
            comp = (annotation.value == null) ? 1 : value.compareTo(annotation.value);
        }

        return comp;
    }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy