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;
023
024import java.util.AbstractMap;
025import java.util.AbstractSet;
026import java.util.Iterator;
027import java.util.Map;
028import java.util.Set;
029
030import org.biojava.bio.program.tagvalue.ChangeTable;
031import org.biojava.utils.ParserException;
032
033/**
034 * <code>AnnotationChanger</code> remaps the values of an
035 * <code>Annotation</code> to new values specified by a
036 * <code>ValueChanger</code>. This will modify the values
037 * associated with properties, but not the property names.
038 * 
039 * For writing light-weigth adaptors to project one type of
040 * Annotation to another using a ChangeTable.
041 *
042 * @since 1.3
043 * @author Matthew Pocock
044 *
045 */
046public class AnnotationChanger extends AbstractAnnotation
047{
048    private final Annotation wrapped;
049    private final ChangeTable changer;
050    private final Map properties;
051  
052    /**
053     * Creates a new <code>AnnotationChanger</code> using the
054     * specified <code>ValueChanger</code> to remap its values.
055     *
056     * @param wrapped an <code>Annotation</code>.
057     * @param changer a <code>ValueChanger</code>.
058     */
059    public AnnotationChanger(Annotation wrapped, ChangeTable changer) {
060        this.wrapped = wrapped;
061        this.changer = changer;
062        this.properties = new MappedHash();
063    }
064
065    /**
066     * <code>getWrapped</code> returns the <code>Annotation</code>
067     * being remapped.
068     *
069     * @return an <code>Annotation</code>.
070     */
071    public Annotation getWrapped() {
072        return wrapped;
073    }
074
075    /**
076     * <code>getMapper</code> returns the <code>ValueChanger</code> being
077     * used to remap the <code>Annotation</code>.
078     *
079     * @return a <code>ValueChanger</code>.
080     */
081    public ChangeTable getChanger() {
082        return changer;
083    }
084
085    /**
086     * <code>getProperties</code> returns the mapped contents of the
087     * underlying <code>Annotation</code> as a <code>Map</code>.
088     *
089     * @return a <code>Map</code>.
090     */
091    public Map getProperties() {
092        return properties;
093    }
094
095    /**
096     * <code>propertiesAllocated</code> is a convenience method to see
097     * if we have allocated the properties <code>Map</code>.
098     *
099     * @return a <code>boolean</code> true if the properties have been
100     * allocated, false otherwise.
101     */
102    public boolean propertiesAllocated() {
103        return true;
104    }
105
106    private class MappedHash extends AbstractMap {
107        public int size() {
108            return wrapped.asMap().size();
109        }
110
111        public Set entrySet() {
112            return new WrappedSet(wrapped.asMap().entrySet());
113        }
114    }
115
116    private class WrappedSet extends AbstractSet {
117        private Set entrySet;
118
119        public WrappedSet(Set entrySet) {
120            this.entrySet = entrySet;
121        }
122
123        public int size() {
124            return entrySet.size();
125        }
126
127        public Iterator iterator() {
128            return new Iterator() {
129                    Iterator i = entrySet.iterator();
130
131                    public boolean hasNext() {
132                        return i.hasNext();
133                    }
134
135                    public Object next() {
136                        final Map.Entry entry = (Map.Entry) i.next();
137
138                        return new Map.Entry() {
139                                public Object getKey() {
140                                    return entry.getKey();
141                                }
142
143                                public Object getValue() {
144                                  try {
145                                    return changer.change(getKey(), entry.getValue());
146                                  } catch (ParserException pe) {
147                                    throw new BioError(pe);
148                                  }
149                                }
150
151                                public Object setValue(Object value) {
152                                    return entry.setValue(value);
153                                }
154                            };
155                    }
156        
157                    public void remove() {
158                        i.remove();
159                    }
160                };
161        }
162    }
163}