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}