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 * Created on 01-21-2010
021 */
022package org.biojava.nbio.core.sequence.template;
023
024import org.biojava.nbio.core.util.Equals;
025import org.biojava.nbio.core.util.Hashcoder;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029import java.util.*;
030
031
032/**
033 *
034 * @author Andy Yates
035 *
036 * @param <C> The compound this set will contain
037 */
038public abstract class AbstractCompoundSet<C extends Compound> implements CompoundSet<C> {
039
040        private final static Logger logger = LoggerFactory.getLogger(AbstractCompoundSet.class);
041
042        private Map<CharSequence, C> charSeqToCompound = new HashMap<CharSequence, C>();
043        private int maxCompoundCharSequenceLength = -1;
044        private Boolean compoundStringLengthEqual = null;
045
046        Map<C,Set<C>> equivalentsMap = new HashMap<C, Set<C>>();
047
048        protected void addCompound(C compound, C lowerCasedCompound, Iterable<C> equivalents) {
049                addCompound(compound);
050                addCompound(lowerCasedCompound);
051
052                addEquivalent(compound, lowerCasedCompound);
053                addEquivalent(lowerCasedCompound, compound);
054
055                for(C equivalent: equivalents) {
056                        addEquivalent(compound, equivalent);
057                        addEquivalent(equivalent, compound);
058                        addEquivalent(lowerCasedCompound, equivalent);
059                        addEquivalent(equivalent, lowerCasedCompound);
060                }
061        }
062
063        protected void addCompound(C compound, C lowerCasedCompound, C... equivalents) {
064                List<C> equiv = new ArrayList<C>(equivalents.length);
065                equiv.addAll(Arrays.asList(equivalents));
066                addCompound(compound, lowerCasedCompound, equiv);
067        }
068
069        protected void addEquivalent(C compound, C equivalent) {
070         Set<C> s = equivalentsMap.get(compound);
071         if ( s == null){
072                 s = new HashSet<C>();
073                 equivalentsMap.put(compound, s);
074         }
075
076                s.add( equivalent);
077        }
078
079        protected void addCompound(C compound) {
080                charSeqToCompound.put(compound.toString(), compound);
081                maxCompoundCharSequenceLength = -1;
082                compoundStringLengthEqual = null;
083        }
084
085        @Override
086public String getStringForCompound(C compound) {
087                return compound.toString();
088        }
089
090        @Override
091public C getCompoundForString(String string) {
092                if(string == null) {
093                        throw new IllegalArgumentException("Given a null CharSequence to process");
094                }
095
096                if (string.length()==0) {
097                        return null;
098                }
099
100                if (string.length() > getMaxSingleCompoundStringLength()) {
101                        throw new IllegalArgumentException("CharSequence supplied is too long.");
102                }
103
104                return charSeqToCompound.get(string);
105        }
106
107        @Override
108public int getMaxSingleCompoundStringLength() {
109                if(maxCompoundCharSequenceLength == -1) {
110                        for(C compound: charSeqToCompound.values()) {
111                                int size = getStringForCompound(compound).length();
112                                if(size > maxCompoundCharSequenceLength) {
113                                        maxCompoundCharSequenceLength = size;
114                                }
115                        }
116                }
117                return maxCompoundCharSequenceLength;
118        }
119
120                @Override
121                public boolean isCompoundStringLengthEqual() {
122                                if(compoundStringLengthEqual == null) {
123                                                int lastSize = -1;
124                                                compoundStringLengthEqual = Boolean.TRUE;
125                                                for(CharSequence c: charSeqToCompound.keySet()) {
126                                                                if(lastSize != c.length()) {
127                                                                                compoundStringLengthEqual = Boolean.FALSE;
128                                                                                break;
129                                                                }
130                                                }
131                                }
132                                return compoundStringLengthEqual;
133                }
134
135        @Override
136public boolean hasCompound(C compound) {
137                C retrievedCompound = getCompoundForString(compound.toString());
138                return retrievedCompound != null;
139        }
140
141        @Override
142public boolean compoundsEquivalent(C compoundOne, C compoundTwo) {
143                assertCompound(compoundOne);
144                assertCompound(compoundTwo);
145                return compoundOne.equals(compoundTwo) || equivalentsMap.get(compoundOne).contains(compoundTwo);
146        }
147
148        @Override
149public Set<C> getEquivalentCompounds(C compound) {
150                return equivalentsMap.get(compound);
151        }
152
153        public boolean compoundsEqual(C compoundOne, C compoundTwo) {
154                assertCompound(compoundOne);
155                assertCompound(compoundTwo);
156                return compoundOne.equalsIgnoreCase(compoundTwo);
157        }
158
159                @Override
160                public boolean isValidSequence(Sequence<C> sequence) {
161                                for (C compound: sequence) {
162                                                if (!hasCompound(compound)) {
163                                                                return false;
164                                                }
165                                }
166                                return true;
167                }
168
169
170
171        @Override
172public List<C> getAllCompounds() {
173                return new ArrayList<C>(charSeqToCompound.values());
174        }
175
176        private void assertCompound(C compound) {
177                if (!hasCompound(compound)) {
178                        // TODO this used to throw an error, now only warning, is this the best solution?
179                                // dmyersturnbull: I think throwing a CompoundNotFoundException is far better
180                        logger.warn("The CompoundSet {} knows nothing about the compound {}", getClass().getSimpleName(), compound);
181                        //throw new CompoundNotFoundError("The CompoundSet "+
182                        //    getClass().getSimpleName()+" knows nothing about the compound "+
183                        //    compound);
184                }
185        }
186
187                @Override
188                public boolean isComplementable() {
189                                return false;
190                }
191
192                @Override
193                public int hashCode() {
194                                int s = Hashcoder.SEED;
195                                s = Hashcoder.hash(s, charSeqToCompound);
196                                s = Hashcoder.hash(s, equivalentsMap);
197                                return s;
198                }
199
200                @Override
201                @SuppressWarnings("unchecked")
202                public boolean equals(Object o) {
203                                if (! (o instanceof AbstractCompoundSet)) return false;
204                                if(Equals.classEqual(this, o)) {
205                                                AbstractCompoundSet<C> that = (AbstractCompoundSet<C>)o;
206                                                return  Equals.equal(charSeqToCompound, that.charSeqToCompound) &&
207                                                                                Equals.equal(equivalentsMap, that.equivalentsMap);
208                                }
209                                return false;
210                }
211
212
213}