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 */
021package org.biojava.nbio.core.sequence.template;
022
023import org.biojava.nbio.core.exceptions.TranslationException;
024import org.biojava.nbio.core.sequence.io.template.SequenceCreatorInterface;
025
026import java.util.*;
027
028
029public abstract class AbstractCompoundTranslator<F extends Compound, T extends Compound>
030                implements CompoundTranslator<F, T> {
031
032        private final SequenceCreatorInterface<T> creator;
033        private final Map<F, List<T>>          mapper;
034        private final CompoundSet<F>              fromCompoundSet;
035        private final CompoundSet<T>              toCompoundSet;
036
037        public AbstractCompoundTranslator(SequenceCreatorInterface<T> creator,
038                        CompoundSet<F> fromCompoundSet, CompoundSet<T> toCompoundSet) {
039                this.creator = creator;
040                this.mapper = new HashMap<F, List<T>>();
041                this.fromCompoundSet = fromCompoundSet;
042                this.toCompoundSet = toCompoundSet;
043        }
044
045        public SequenceCreatorInterface<T> getCreator() {
046                return creator;
047        }
048
049        public CompoundSet<F> getFromCompoundSet() {
050                return fromCompoundSet;
051        }
052
053        public CompoundSet<T> getToCompoundSet() {
054                return toCompoundSet;
055        }
056
057        @SuppressWarnings("unchecked")
058        protected void addStrings(String source, String... targets) {
059                F f = getFromCompoundSet().getCompoundForString(source);
060                for (String t : targets) {
061                        addCompounds(f, getToCompoundSet().getCompoundForString(t));
062                }
063        }
064
065        protected void addCompounds(F source, T... targets) {
066
067         List<T> l = mapper.get(source);
068         if ( l == null) {
069                 l = new ArrayList<T>();
070                 mapper.put(source, l);
071         }
072                 l.addAll(Arrays.asList(targets));
073        }
074
075        @Override
076        public List<T> translateMany(F fromCompound) {
077                if (!mapper.containsKey(fromCompound)) {
078                        throw new TranslationException("Can not translate compound "+fromCompound);
079                }
080                return mapper.get(fromCompound);
081        }
082
083                @Override
084        public T translate(F fromCompound) {
085                List<T> compounds = translateMany(fromCompound);
086                if (compounds.isEmpty()) {
087                        throw new TranslationException("No compounds found for " + fromCompound);
088                }
089                else if (compounds.size() > 1) {
090                        throw new TranslationException("Too many compounds found for "
091                                        + fromCompound);
092                }
093                else {
094                        return compounds.get(0);
095                }
096        }
097
098                @Override
099        public List<Sequence<T>> createSequences(Sequence<F> originalSequence) {
100                List<List<T>> workingList = new ArrayList<List<T>>();
101                for (F source : originalSequence) {
102                        List<T> compounds = translateMany(source);
103
104                        // Translate source to a list of possible compounds; if we have 1 then
105                        // just add onto the list. If we have n then start new paths in all
106                        // sequences i.e.
107                        //
108                        // MTAS (A & S have 2 routes) makes
109                        // AUG UGG GAU AGU
110                        // AUG UGG GAC AGU
111                        // AUG UGG GAU AGC
112                        // AUG UGG GAC AGC
113                        if (compounds.isEmpty()) {
114                                throw new TranslationException("Compound " + source + " resulted in "
115                                                + "no target compounds");
116                        }
117                        addCompoundsToList(compounds, workingList);
118                }
119
120                postProcessCompoundLists(workingList);
121
122                return workingListToSequences(workingList);
123        }
124
125        protected abstract void postProcessCompoundLists(List<List<T>> compoundLists);
126
127        protected void addCompoundsToList(List<T> compounds, List<List<T>> workingList) {
128                int size = compounds.size();
129                List<List<T>> currentWorkingList = new ArrayList<List<T>>();
130                for (int i = 0; i < size; i++) {
131                        boolean last = (i == (size - 1));
132                        // If last run we add the compound to the top set of lists & then
133                        // add the remaining ones in
134                        if (last) {
135                                addCompoundToLists(workingList, compounds.get(i));
136                                if (!currentWorkingList.isEmpty()) {
137                                        workingList.addAll(currentWorkingList);
138                                }
139                        }
140                        // Otherwise duplicate the current sequence set and add this compound
141                        else {
142                                List<List<T>> duplicate = duplicateList(workingList);
143                                addCompoundToLists(duplicate, compounds.get(i));
144                                currentWorkingList.addAll(duplicate);
145                        }
146                }
147        }
148
149        protected List<Sequence<T>> workingListToSequences(List<List<T>> workingList) {
150                List<Sequence<T>> sequences = new ArrayList<Sequence<T>>();
151                for (List<T> seqList : workingList) {
152                        sequences.add(getCreator().getSequence(seqList));
153                }
154                return sequences;
155        }
156
157        private List<List<T>> duplicateList(List<List<T>> incoming) {
158                List<List<T>> outgoing = new ArrayList<List<T>>();
159                for (List<T> current : incoming) {
160                        outgoing.add(new ArrayList<T>(current));
161                }
162                return outgoing;
163        }
164
165        protected void addCompoundToLists(List<List<T>> list, T compound) {
166
167                if (list.isEmpty()) {
168                        list.add(new ArrayList<T>());
169                }
170
171                for (List<T> current : list) {
172                        current.add(compound);
173                }
174        }
175
176                @Override
177        public Sequence<T> createSequence(Sequence<F> originalSequence) {
178                Collection<Sequence<T>> sequences = createSequences(originalSequence);
179                if (sequences.size() > 1) {
180                        throw new TranslationException("Too many sequences created; "
181                                        + "createSequence() assumes only one sequence can be created");
182                }
183                else if (sequences.isEmpty()) {
184                        throw new TranslationException("No sequences created");
185                }
186                return sequences.iterator().next();
187        }
188
189}