001package org.biopax.paxtools.pattern.constraint;
002
003import org.biopax.paxtools.model.level3.ModificationFeature;
004import org.biopax.paxtools.model.level3.PhysicalEntity;
005import org.biopax.paxtools.model.level3.SequenceModificationVocabulary;
006import org.biopax.paxtools.pattern.Match;
007import org.biopax.paxtools.pattern.util.DifferentialModificationUtil;
008
009import java.util.HashSet;
010import java.util.Set;
011
012/**
013 * This class checks if there exists a desired type of modification change among two PhysicalEntity.
014 *
015 * var0: First simple PhysicalEntity
016 * Var1: Second simple PhysicalEntity
017 *
018 * @author Ozgun Babur
019 */
020public class ModificationChangeConstraint extends ConstraintAdapter
021{
022        /**
023         * Partial names of the features to be considered.
024         */
025        protected String[] featureSubstring;
026
027        /**
028         * Gain or loss?
029         */
030        protected Type type;
031
032        /**
033         * Constructor with the desired change and maps to activating and inactivating features. If the
034         * feature substrings are not provided, any feature is qualified.
035         *
036         * @param type either gain, loss, or any
037         * @param featureSubstring partial names of the features desired to be changed
038         */
039        public ModificationChangeConstraint(Type type, String... featureSubstring)
040        {
041                super(2);
042
043                this.type = type;
044
045                for (int i = 0; i < featureSubstring.length; i++)
046                {
047                        featureSubstring[i] = featureSubstring[i].toLowerCase();
048                }
049
050                this.featureSubstring = featureSubstring;
051        }
052
053        /**
054         * Checks the any of the changed modifications match to any of the desired modifications.
055         * @param match current pattern match
056         * @param ind mapped indices
057         * @return true if a modification change is among desired modifications
058         */
059        @Override
060        public boolean satisfies(Match match, int... ind)
061        {
062                PhysicalEntity pe1 = (PhysicalEntity) match.get(ind[0]);
063                PhysicalEntity pe2 = (PhysicalEntity) match.get(ind[1]);
064
065                Set<ModificationFeature>[] mods =
066                        DifferentialModificationUtil.getChangedModifications(pe1, pe2);
067
068                Set<String> terms;
069
070                if (type == Type.GAIN) terms = collectTerms(mods[0]);
071                else if (type == Type.LOSS) terms = collectTerms(mods[1]);
072                else terms = collectTerms(mods);
073
074                return termsContainDesired(terms);
075        }
076
077        private Set<String> collectTerms(Set<ModificationFeature>... mods)
078        {
079                Set<String> terms = new HashSet<String>();
080                collectTerms(mods[0], terms);
081                if (mods.length > 1) collectTerms(mods[1], terms);
082                return terms;
083        }
084
085        private void collectTerms(Set<ModificationFeature> mods, Set<String> terms)
086        {
087                for (ModificationFeature mf : mods)
088                {
089                        SequenceModificationVocabulary type = mf.getModificationType();
090                        if (type != null)
091                        {
092                                for (String term : type.getTerm())
093                                {
094                                        terms.add(term.toLowerCase());
095                                }
096                        }
097                }
098        }
099
100        /**
101         * Checks if any element in the set contains the term.
102         * @param terms changed terms
103         * @return true if any changed terms contains a desired substring
104         */
105        private boolean termsContainDesired(Set<String> terms)
106        {
107                if (terms.isEmpty()) return false;
108                if (featureSubstring.length == 0) return true;
109
110                for (String term : terms)
111                {
112                        for (String sub : featureSubstring)
113                        {
114                                if (term.contains(sub)) return true;
115                        }
116                }
117                return false;
118        }
119
120        public enum Type
121        {
122                GAIN,
123                LOSS,
124                ANY
125        }
126}