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.symbol;
023
024import java.util.HashSet;
025import java.util.Iterator;
026import java.util.Set;
027
028/**
029 * an abstract class implementing basic functionality
030 * of a translation table that translates Symbols from
031 * one Alphabet to another.
032 * <p>
033 * The mapping between the alphabets can be either 
034 * one-to-one or many-to one.
035 *
036 * @author David Huen
037 */
038public abstract class AbstractManyToOneTranslationTable 
039    extends AbstractTranslationTable 
040    implements ManyToOneTranslationTable
041{
042    public abstract Alphabet getSourceAlphabet();
043    public abstract Alphabet getTargetAlphabet();
044    /**
045     * this method is expected to reverse-translate any symbol
046     * in the source alphabet.  Failure can be indicated
047     * by returning a null if, for example, your method
048     * only handles AtomicSymbols and you want BasisSymbols
049     * to be taken apart.  If you are sure the symbol is
050     * illegal, you can throw the IllegalSymbolException
051     * immediately to bypass further processing.
052
053     * <p>
054     * As an optimisation, if your method is capable of
055     * immediately translating an ambiguity Symbol, just
056     * return it and the alternate route of establishing
057     * the translation through doing an ambiguity
058     * lookup will be avoided.
059     */
060    protected abstract Set doUntranslate(Symbol sym) throws IllegalSymbolException;
061
062    /**
063     * returns a Set of Atomic Symbols that are the reverse translation
064     * of the specified Symbol in the target alphabet.
065     */
066    public Set untranslate(final Symbol sym)
067    throws IllegalSymbolException {
068        // make an attempt to translate immediately
069        Set s = doUntranslate(sym);
070
071        // translation failed, validate and try an ambiguity lookup
072        if(s == null) {
073            if(sym instanceof AtomicSymbol) { //changed this from s to sym, since we already checked and s is null
074                getSourceAlphabet().validate(sym);
075
076                // the symbol was valid and still we can't handle it, bail out!
077                throw new IllegalSymbolException("Unable to map " + sym.getName());
078            } else {
079                if(sym == null) {
080                    throw new NullPointerException("Can't translate null");
081                }
082                s = new HashSet();
083                for (Iterator i = ((FiniteAlphabet) sym.getMatches()).iterator(); i.hasNext(); ) {
084                    Symbol is = (Symbol) i.next();
085                    s.add(this.untranslate(is));
086                }
087            }
088        }
089        return s;
090    }
091}