AbstractSemanticAnnotationHelper.java

/*
 *  AbstractSemanticAnnotationHelper.java
 *
 *  Copyright (c) 2007-2011, The University of Sheffield.
 *
 *  This file is part of GATE Mímir (see http://gate.ac.uk/family/mimir.html), 
 *  and is free software, licenced under the GNU Lesser General Public License,
 *  Version 3, June 2007 (also included with this distribution as file
 *  LICENCE-LGPL3.html).
 *
 *  Valentin Tablan, 5 Aug 2009
 *
 *  $Id: AbstractSemanticAnnotationHelper.java 17261 2014-01-30 14:05:14Z valyt $
 */
package gate.mimir;

import gate.Document;
import gate.mimir.index.AtomicAnnotationIndex;
import gate.mimir.index.Mention;
import gate.mimir.search.QueryEngine;
import gate.mimir.util.DefaultMentionDescriber;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;


/**
 * Simple abstract class that provides:
 * <ul>
 *   <li>an empty implementation for {@link #documentStart(Document)}</li>
 *   <li>an empty implementation for {@link #documentEnd()}</li>
 *   <li>an implementation for {@link #getMentions(String, Map, QueryEngine)},
 *   which simply calls {@link #getMentions(String, List, QueryEngine)} after
 *   creating the appropriate constraints.</li>
 * </ul>
 * 
 * Subclasses are required to provide a no-argument constructor. When 
 * implementing the {@link #init(Indexer)} and {@link #init(QueryEngine)} 
 * methods, they must first call super.init(...). 
 * 
 * <p>If a particular helper does not support certain feature types it should
 * use the {@link #concatenateArrays} method to combine those features with
 * another kind that it can support, during the init(Indexer) call.  For example, 
 * helpers that do not have access to a full semantic repository may choose to 
 * treat URI features as if they were simply text features with no semantics.</p>
 */
public abstract class AbstractSemanticAnnotationHelper implements
    SemanticAnnotationHelper {
	
  /**
   * Interface for supporting classes used by 
   * {@link AbstractSemanticAnnotationHelper} and its sub-classes to provide a 
   * pluggable implementation for 
   * {@link SemanticAnnotationHelper#describeMention(String)}. 
   */
  public static interface MentionDescriber extends Serializable {
    public String describeMention(AbstractSemanticAnnotationHelper helper, 
        String mentionUri, String[] descriptiveFeatureNames, 
        String[] descriptiveFeatureValues);
  }
  
	private static final long serialVersionUID = -5432862771431426914L;

  private transient boolean isInited = false;
  
  
  protected final boolean isInited() {
    return isInited;
  }

  /**
   * The list of names for the nominal features.
   */
  protected String[] nominalFeatureNames;

  /**
   * The list of names for the numeric features.
   */
  protected String[] integerFeatureNames;

  /**
   * The list of names for the numeric features.
   */
  protected String[] floatFeatureNames;
  
  /**
   * The list of names for the text features.
   */
  protected String[] textFeatureNames;
	
  /**
   * The list of names for the URI features.
   */
  protected String[] uriFeatureNames;
  
  /**
   * The type of the annotations handled by this helper.
   */
  protected String annotationType;
	
  /**
   * The list of names for all the features that should be used when describing
   * an annotation mention (see {@link #describeMention(String)}).
   */
  protected String[] descriptiveFeatures;
  
  protected MentionDescriber mentionDescriber;
  
  
  /**
   * The working mode for this helper (defaults to {@link Mode#ANNOTATION}).
   */
  protected Mode mode = Mode.ANNOTATION;
  
  public Mode getMode() {
    return mode;
  }

  public void setMode(Mode mode) {
    this.mode = mode;
  }

  public String getAnnotationType() {
    return annotationType;
  }

  public void setAnnotationType(String annotationType) {
    this.annotationType = annotationType;
  }  
  
  public void setAnnType(String annotationType) {
    setAnnotationType(annotationType);
  }

  public String[] getNominalFeatures() {
    return nominalFeatureNames;
  }

  public void setNominalFeatures(String[] nominalFeatureNames) {
    this.nominalFeatureNames = nominalFeatureNames;
  }

  public String[] getIntegerFeatures() {
    return integerFeatureNames;
  }



  public void setIntegerFeatures(String[] integerFeatureNames) {
    this.integerFeatureNames = integerFeatureNames;
  }
  
  public String[] getFloatFeatures() {
    return floatFeatureNames;
  }

  public void setFloatFeatures(String[] floatFeatureNames) {
    this.floatFeatureNames = floatFeatureNames;
  }

  
  public String[] getTextFeatures() {
    return textFeatureNames;
  }

  public void setTextFeatures(String[] textFeatureNames) {
    this.textFeatureNames = textFeatureNames;
  }
  
  /**
   * @return the uriFeatureNames
   */
  public String[] getUriFeatures() {
    return uriFeatureNames;
  }

  public void setUriFeatures(String[] uriFeatureNames) {
    this.uriFeatureNames = uriFeatureNames;
  }
  
  /**
   * Gets the names of features that should be used when describing an
   * annotation mention.
   *  
   * @return the descriptiveFeatures
   */
  protected String[] getDescriptiveFeatures() {
    return descriptiveFeatures;
  }

  /**
   * Sets the names of features that should be used when describing an
   * annotation mention. This should be called <strong>before</strong> the 
   * helper is initialised (i.e. before calling {@link #init(QueryEngine)}).
   * 
   * If no custom value has been set before {@link #init(QueryEngine)} is 
   * called, then all features are used as descriptive features.
   * 
   * @param descriptiveFeatures the descriptiveFeatures to set
   */
  public void setDescriptiveFeatures(String[] descriptiveFeatures) {
    this.descriptiveFeatures = descriptiveFeatures;
  }

  
  
  /**
   * @return the mentionDescriber
   */
  public MentionDescriber getMentionDescriber() {
    return mentionDescriber;
  }

  /**
   * Sets the {@link MentionDescriber} to be used as the implementation of
   * {@link SemanticAnnotationHelper#describeMention(String)}.
   * If set to <code>null</code>, then an instance of 
   * {@link DefaultMentionDescriber} is automatically created and used.
   *  
   * @param mentionDescriber the custom mentionDescriber to use
   */
  public void setMentionDescriber(MentionDescriber mentionDescriber) {
    this.mentionDescriber = mentionDescriber;
  }

  /* (non-Javadoc)
   * @see gate.mimir.SemanticAnnotationHelper#documentEnd()
   */
  public void documentEnd() {}

  public void documentStart(Document document) { }


  /* (non-Javadoc)
   * @see gate.mimir.SemanticAnnotationHelper#getMentions(java.lang.String, java.util.Map, gate.mimir.search.QueryEngine)
   */
  public List<Mention> getMentions(String annotationType,
          Map<String, String> constraints, QueryEngine engine) {
    //convert the simple constraints to actual implementations.
    List<Constraint> predicates = new ArrayList<Constraint>(constraints.size());
    for(Entry<String, String> entry : constraints.entrySet()){
      predicates.add(new Constraint(ConstraintType.EQ, entry.getKey(), entry.getValue()));
    }
    return getMentions(annotationType, predicates, engine);
  }

  
  
  /* (non-Javadoc)
   * @see gate.mimir.SemanticAnnotationHelper#describeMention(java.lang.String)
   */
  @Override
  public String describeMention(String mentionUri) {
    if(mentionDescriber == null) {
      mentionDescriber = new DefaultMentionDescriber();
    }
    return mentionDescriber.describeMention(this, mentionUri, 
      getDescriptiveFeatures(), getDescriptiveFeatureValues(mentionUri));
  }
  
  /**
   * Calculates the textual representations for the values of features that are
   * part of the description of an annotation mention. The list of features for
   * which the values should be returned is {@link #descriptiveFeatures}.
   * 
   * This implementation always returns <code>null</code> as the abstract class
   * has no way of accessing the actual feature values. Subclasses should 
   * provide an actual implementation to support proper mention descriptions. 
   * 
   * @param mentionUri the URI for the mention that needs to be described.
   * 
   * @return an array of strings parallel with {@link #descriptiveFeatures}, or
   * null if the feature values are not known.
   */
  protected String[] getDescriptiveFeatureValues(String mentionUri) {
    return null;
  }
  
  /**
   * Helper method to concatenate a number of arrays into one, for helpers
   * that don't support all the feature types and want to combine some of
   * them together.
   * @return null if all the supplied arrays are either null or empty,
   *        otherwise a single array containing the concatenation of
   *        all the supplied arrays in order.
   */
  protected static String[] concatenateArrays(String[]... arrays) {
    int totalLength = 0;
    for(String[] arr : arrays) {
      if(arr != null) totalLength += arr.length;
    }
    if(totalLength == 0) return null;
    String[] concat = new String[totalLength];
    int start = 0;
    for(String[] arr : arrays) {
      if(arr != null) {
        System.arraycopy(arr, 0, concat, start, arr.length);
        start += arr.length;
      }
    }
    return concat;
  }

  private void checkInit() {
    if(isInited) throw new IllegalStateException(
      "This helper has already been initialised!");
    
    isInited = true;
  }

  
  @Override
  public void init(AtomicAnnotationIndex index) {
    checkInit();
    // calculate the list of descriptive features if needed
    if(descriptiveFeatures == null) {
      List<String> featNames = new ArrayList<String>();
      if(nominalFeatureNames != null){
        Collections.addAll(featNames, nominalFeatureNames);
      }
      if(integerFeatureNames != null) {
        Collections.addAll(featNames, integerFeatureNames);
      }
      if(floatFeatureNames != null) {
        Collections.addAll(featNames, floatFeatureNames);
      }
      if(textFeatureNames != null) {
        Collections.addAll(featNames, textFeatureNames);
      }
      if(uriFeatureNames != null) {
        Collections.addAll(featNames, uriFeatureNames);
      }
      descriptiveFeatures = featNames.toArray(new String[featNames.size()]);
    }
  }
}