/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 */


package org.biojava.bio;

import java.util.*;
import java.io.*;

/**
 * A no-frills implementation of Annotation that is just a wrapper around a Map.
 * <P>
 * It will allow you to set any property, but will throw exceptions if you try
 * to retrieve a property that is not set.
 *
 * @author Matthew Pocock
 */
public class SimpleAnnotation implements Annotation, Serializable {
  /**
   * The properties map.
   * <P>
   * This may be null if no property values have yet been set.
   */
  private Map properties;
  
  /**
   * Retrieves properties, potentialy creating it if it was null.
   *
   * @return the properties Map
   */
  protected final Map getProperties() {
    if(!propertiesAllocated())
      properties = new HashMap();
    return properties;
  }

  /**
   * A convenience method to see if we have allocated the properties map yet.
   *
   * @returns true if properties is set and false otherwise
   */
  protected final boolean propertiesAllocated() {
    return properties != null;
  }

  /**
   * @param key The key whose property to retrieve.
   * @throws NoSuchElementException if the property 'key' does not exist
   */
  public Object getProperty(Object key) throws NoSuchElementException {
    if(propertiesAllocated()) {
      Map prop = getProperties();
      if(prop.containsKey(key)) {
        return prop.get(key);
      }
    }
    throw new NoSuchElementException("Property " + key + " unknown");
  }

  public void setProperty(Object key, Object value) {
    getProperties().put(key, value);
  }

  public Set keys() {
    if(propertiesAllocated()) {
      return properties.keySet();
    } else {
      return Collections.EMPTY_SET;
    }
  }
  
  public String toString() {
    StringBuffer sb = new StringBuffer("{");
    Map prop = getProperties();
    Iterator i = prop.keySet().iterator();
    if(i.hasNext()) {
      Object key = i.next();
      sb.append(key + "=" + prop.get(key));
    }
    while(i.hasNext()) {
      Object key = i.next();
      sb.append("," + key + "=" + prop.get(key));
    }
    sb.append("}");
    return sb.toString();
  }
  
  public Map asMap() {
    return new HashMap(getProperties());
  }
  
  public SimpleAnnotation() {
  }
  
  public SimpleAnnotation(Annotation ann) throws IllegalArgumentException {
    if(ann == null) {
      throw new IllegalArgumentException(
        "Null annotation not allowed. Use Annotation.EMPTY_ANNOTATION instead."
      );
    }
    if(ann == Annotation.EMPTY_ANNOTATION) {
      return;
    }
    for(Iterator i = ann.keys().iterator(); i.hasNext(); ) {
      Object key = i.next();
      try {
        setProperty(key, ann.getProperty(key));
      } catch (IllegalArgumentException iae) {
        throw new BioError(
          iae,
          "Property was there and then dissapeared: " + key
        );
      }
    }
  }
  
  public SimpleAnnotation(Map annMap) {
    if(annMap == null) {
      throw new IllegalArgumentException(
        "Null annotation Map not allowed. Use an empy map instead."
      );
    }
    if(annMap.isEmpty()) {
      return;
    }
    for(Iterator i = annMap.keySet().iterator(); i.hasNext(); ) {
      Object key = i.next();
      setProperty(key, annMap.get(key));
    }
  }
}

