/* Copyright (C) 2003 Univ. of Massachusetts Amherst, Computer Science Dept.
   This file is part of "MALLET" (MAchine Learning for LanguagE Toolkit).
   http://www.cs.umass.edu/~mccallum/mallet
   This software is provided under the terms of the Common Public License,
   version 1.0, as published by http://www.opensource.org.  For further
   information, see the file `LICENSE' included with this distribution. */
package cc.mallet.extract;


import java.io.Serializable;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

import cc.mallet.types.Label;
import cc.mallet.types.LabelAlphabet;
import cc.mallet.types.Sequence;

/**
 * Created: Nov 12, 2004
 *
 * @author <A HREF="mailto:casutton@cs.umass.edu>casutton@cs.umass.edu</A>
 * @version $Id: BIOTokenizationFilter.java,v 1.1 2007/10/22 21:37:44 mccallum Exp $
 */
public class BIOTokenizationFilter implements TokenizationFilter, Serializable {

  public LabeledSpans constructLabeledSpans (LabelAlphabet dict, Object document, Label backgroundTag,
                                             Tokenization input, Sequence seq)
  {
    LabeledSpans labeled = new LabeledSpans (document);
    addSpansFromTags (labeled, input, seq, dict, backgroundTag);
    return labeled;
  }


  private void addSpansFromTags (LabeledSpans labeled, Tokenization input, Sequence tags, LabelAlphabet dict,
                                 Label backgroundTag)
  {
    int i = 0;
    int docidx = 0;
    while (i < tags.size ()) {
      Label thisTag = dict.lookupLabel (tags.get (i).toString ());
      int startTokenIdx = i;
      while (++i < tags.size ()) {
        Label nextTag = dict.lookupLabel (tags.get (i).toString ());
        if (isBeginTag (nextTag) || !tagsMatch (thisTag, nextTag)) break;
      }
      int endTokenIdx = i;
      Span span = createSpan (input, startTokenIdx, endTokenIdx);
      addBackgroundIfNecessary (labeled, (StringSpan) span, docidx, backgroundTag);
      docidx = ((StringSpan) span).getEndIdx ();

      if (isBeginTag (thisTag) || isInsideTag (thisTag)) {
        thisTag = trimTag (dict, thisTag);
      }
      labeled.add (new LabeledSpan (span, thisTag, thisTag == backgroundTag));
    }
  }

  protected Span createSpan (Tokenization input, int startTokenIdx, int endTokenIdx)
  {
    return input.subspan (startTokenIdx, endTokenIdx);
  }


  private Label trimTag (LabelAlphabet dict, Label tag)
  {
    String name = (String) tag.getEntry ();
    return dict.lookupLabel (name.substring (2));
  }


  private boolean tagsMatch (Label tag1, Label tag2)
  {
    String name1 = (String) tag1.getEntry ();
    String name2 = (String) tag2.getEntry ();

    if (isBeginTag (tag1) || isInsideTag (tag1)) {
      name1 = name1.substring (2);
    }
    if (isInsideTag (tag2)) {
      name2 = name2.substring (2);
    }

    return name1.equals (name2);
  }


  private boolean isBeginTag (Label lbl)
  {
    String name = (String) lbl.getEntry ();
    return name.startsWith ("B-");
  }

  private boolean isInsideTag (Label lbl)
  {
    String name = (String) lbl.getEntry ();
    return name.startsWith ("I-");
  }

  private void addBackgroundIfNecessary (LabeledSpans labeled, StringSpan span, int docidx, Label background)
  {
    int nextIdx = span.getStartIdx ();
    if (docidx < nextIdx) {
      Span newSpan = new StringSpan ((CharSequence) span.getDocument (), docidx, nextIdx);
      labeled.add (new LabeledSpan (newSpan, background, true));
    }
  }

  // Serialization garbage

  private static final long serialVersionUID = -8726127297313150023L;
  private static final int CURRENT_SERIAL_VERSION = 1;
  
  private void writeObject (ObjectOutputStream out) throws IOException
  {
	  out.defaultWriteObject ();
	  out.writeInt (CURRENT_SERIAL_VERSION);
  }
  
  
  private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException
  {
	  in.defaultReadObject ();
	  in.readInt (); // read version
  }

}
