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 *
033 * @author Matthew Pocock
034 * @author Keith James (docs)
035 * @author Thomas Down
036 * @author Greg Cox
037 * @author Mark Schreiber
038 * @author David Huen (refactoring)
039 */
040public abstract class AbstractReversibleTranslationTable 
041    extends AbstractTranslationTable 
042    implements ReversibleTranslationTable
043{
044    public abstract Alphabet getSourceAlphabet();
045    public abstract Alphabet getTargetAlphabet();
046    /**
047     * this method is expected to reverse-translate any symbol
048     * in the source alphabet.  Failure can be indicated
049     * by returning a null if, for example, your method
050     * only handles AtomicSymbols and you want BasisSymbols
051     * to be taken apart.  If you are sure the symbol is
052     * illegal, you can throw the IllegalSymbolException
053     * immediately to bypass further processing.
054
055     * <p>
056     * As an optimisation, if your method is capable of
057     * immediately translating an ambiguity Symbol, just
058     * return it and the alternate route of establishing
059     * the translation through doing an ambiguity
060     * lookup will be avoided.
061     */
062    protected abstract Symbol doUntranslate(Symbol sym) throws IllegalSymbolException;
063
064    public Symbol untranslate(final Symbol sym)
065    throws IllegalSymbolException {
066        // make an attempt to translate immediately
067        Symbol s = doUntranslate(sym);
068
069        // translation failed, validate and try an ambiguity lookup
070        if(s == null) {
071            if(sym instanceof AtomicSymbol) { //changed this from s to sym, since we already checked and s is null
072                getSourceAlphabet().validate(sym);
073
074                // the symbol was valid and still we can't handle it, bail out!
075                throw new IllegalSymbolException("Unable to map " + sym.getName());
076            } else {
077                if(sym == null) {
078                    throw new NullPointerException("Can't translate null");
079                }
080                Set syms = new HashSet();
081                for (Iterator i = ((FiniteAlphabet) sym.getMatches()).iterator(); i.hasNext(); ) {
082                    Symbol is = (Symbol) i.next();
083                    syms.add(this.untranslate(is));
084                }
085                s = getTargetAlphabet().getAmbiguity(syms);
086            }
087        }
088        return s;
089    }
090}