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}