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