001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021
022package org.biojava.bio.program.tagvalue;
023
024import java.util.List;
025import java.util.Map;
026
027import org.biojava.utils.ParserException;
028import org.biojava.utils.SmallMap;
029
030/**
031 * <p>
032 * A mapping between keys and actions to turn old values into new values.
033 * </p>
034 *
035 * @author Matthew Pocock
036 * @since 1.3
037 */
038public class ChangeTable {
039  public static final Changer STRING_TO_INT = new Changer() {
040    public Object change(Object val) {
041      return new Integer((String) val);
042    }
043  };
044
045  private final Map changers;
046  private final Map splitters;
047
048  public ChangeTable() {
049    this.changers = new SmallMap();
050    this.splitters = new SmallMap();
051  }
052  
053
054  /**
055   * Set the Changer to be used for all values of a particular tag.
056   *
057   * @param tag the tag Object which will have all values changed
058   * @param changer the Changer used to change the values
059   */
060  public void setChanger(Object tag, Changer changer) {
061    changers.put(tag, changer);
062  }
063  
064  /**
065   * Set the Splitter to be used for all values of a particular tag.
066   *
067   * @param tag the tag Object which will have all values split
068   * @param splitter the Splitter used to split the values
069   */
070  public void setSplitter(Object tag, Splitter splitter) {
071    splitters.put(tag, splitter);
072  }
073  
074  /**
075   * Get the Changer currently registered to handle a tag.
076   *
077   * @param tag  the tag Object for which values would be changed
078   * @return the associated Changer or null
079   */
080  public Changer getChanger(Object tag) {
081    return (Changer) changers.get(tag);
082  }
083  
084  /**
085   * Get the Splitter currently registered to handle a tag.
086   *
087   * @param tag  the tag Object for which values would be split
088   * @return the associated Splitter or null
089   */
090  public Splitter getSplitter(Object tag) {
091    return (Splitter) splitters.get(tag);
092  }
093  
094  public Object change(Object tag, Object value)
095  throws ParserException {
096    Changer c = (Changer) changers.get(tag);
097    if(c != null) {
098      return c.change(value);
099    }
100    
101    Splitter s = (Splitter) splitters.get(tag);
102    if(s != null) {
103      return s.split(value);
104    }
105    
106    return value;
107  }
108  
109  /**
110   * Callback used to produce a new value from an old one.
111   *
112   * @author Matthew Pocock
113   * @since 1.3
114   */
115  public static interface Changer {
116    /**
117     * <p>
118     * Produce a modified value from an old value.
119     * </p>
120     *
121     * <p>
122     * It is strongly recommended that this method is re-entrant and does not
123     * modify the state of the Changer in a way that would affect future return
124     * -values.
125     * </p>
126     *
127     * @param value  the old value Object
128     * @return  the new value Object
129     * @throws ParserException if value could not be changed
130     */
131    public Object change(Object value)
132    throws ParserException;
133  }
134  
135  /**
136   * Callback used to produce a list of values from a single old one.
137   *
138   * @author Matthew Pocock
139   * @since 1.3
140   */
141  public static interface Splitter {
142    /**
143     * <p>
144     * Produce a list of values from an old value.
145     * </p>
146     *
147     * <p>
148     * It is strongly recommended that this method is re-entrant and does not
149     * modify the state of the Splitter in a way that would affect future return
150     * -values.
151     * </p>
152     *
153     * @param value  the old value Object
154     * @return  a List of value Objects produced by splitting the old value
155     *          Object
156     * @throws ParserException if the value could not be split
157     */
158    public List split(Object value)
159    throws ParserException;
160  }
161  
162  /**
163   * An implementation of Changer that applies a list of Changer instances to
164   * the value in turn.
165   *
166   * @author Matthew Pocock
167   * @since 1.3
168   */
169  public static class ChainedChanger
170  implements Changer {
171    private Changer[] changers;
172    
173    public ChainedChanger(Changer[] changers) {
174      this.changers = new Changer[changers.length];
175      
176      System.arraycopy(changers, 0, this.changers, 0, changers.length);
177    }
178    
179    public Object change(Object value)
180    throws ParserException {
181      for(int i = 0; i < changers.length; i++) {
182        value = changers[i].change(value);
183      }
184      
185      return value;
186    }
187  }
188}