001package org.biopax.paxtools.io.sif.level2; 002 003import org.biopax.paxtools.io.sif.BinaryInteractionType; 004import org.biopax.paxtools.io.sif.InteractionSet; 005import org.biopax.paxtools.io.sif.SimpleInteraction; 006import org.biopax.paxtools.model.Model; 007import org.biopax.paxtools.model.level2.*; 008 009import java.util.*; 010 011import static org.biopax.paxtools.io.sif.BinaryInteractionType.METABOLIC_CATALYSIS; 012import static org.biopax.paxtools.io.sif.BinaryInteractionType.STATE_CHANGE; 013 014/** 015 * A controls a conversion which B is at left or right or both. - 016 * Controls.StateChange (B at both sides (one side may be as a member of a 017 * complex), or B is complex) - Controls.MetabolicChange (B at one side only) 018 * @author Ozgun Babur Date: Dec 29, 2007 Time: 1:27:55 AM 019 */ 020public class ControlRule extends InteractionRuleL2Adaptor 021{ 022 /** 023 * Supported interaction types. 024 */ 025 private static List<BinaryInteractionType> binaryInteractionTypes = 026 Arrays.asList(METABOLIC_CATALYSIS, STATE_CHANGE); 027 028 /** 029 * Option to mine METABOLIC_CHANGE type. 030 */ 031 private boolean mineMetabolicChange; 032 033 /** 034 * Option to mine STATE_CHANGE type. 035 */ 036 private boolean mineStateChange; 037 038 /** 039 * Initializes option. 040 * @param options options map 041 */ 042 @Override public void initOptionsNotNull(Map options) 043 { 044 mineStateChange = !options.containsKey(STATE_CHANGE) || 045 options.get(STATE_CHANGE).equals(Boolean.TRUE); 046 047 mineMetabolicChange = !options.containsKey(METABOLIC_CATALYSIS) || 048 options.get(METABOLIC_CATALYSIS).equals(Boolean.TRUE); 049 } 050 051 /** 052 * When options map is null, then all rules are generated. Otherwise only rules 053 * that are contained in the options map as a key are generated. 054 * @param interactionSet set to fill in 055 * @param A first physical entity 056 * @param model biopax graph - may be null, has no use here 057 */ 058 public void inferInteractionsFromPE(InteractionSet interactionSet, physicalEntity A, Model model) 059 { 060 061 // Iterate over all associated controls 062 063 for (control cont : A.getAllInteractions(control.class)) 064 { 065 // Iterate over all affected conversions of this control 066 067 for (conversion conv : getAffectedConversions(cont, null)) 068 { 069 Set<physicalEntity> presenceSet = collectEntities(conv.getLEFT(), null); 070 collectEntities(conv.getRIGHT(), presenceSet); 071 072 // Collect left and right simple physical entities of conversion in lists 073 074 List<physicalEntity> left = collectSimpleEntities(conv.getLEFT()); 075 List<physicalEntity> right = collectSimpleEntities(conv.getRIGHT()); 076 077 // Detect physical entities which appear on both sides. 078 079 List<physicalEntity> bothsided = new ArrayList<physicalEntity>(); 080 081 for (physicalEntity B : left) 082 { 083 if (right.contains(B)) 084 { 085 bothsided.add(B); 086 } 087 } 088 089 // Create simple interactions 090 // Try creating a rule for each physical entity in presence list. 091 092 for (physicalEntity B : presenceSet) 093 { 094 // Consider only molecules that is changed by the conversion 095 if (!entityHasAChange(B, conv)) 096 { 097 continue; 098 } 099 100 // Affecting a complex is accepted as type of state change. 101 // If it is simple, then we check if it is also on both sides, regarding the 102 // possibility that it may be nested in a complex. 103 104 if (B instanceof complex || bothsided.contains(B)) 105 { 106 if (mineStateChange) 107 { 108 SimpleInteraction sc = new SimpleInteraction(A, B, STATE_CHANGE); 109 sc.addMediator(cont); 110 sc.addMediator(conv); 111 interactionSet.add(sc); 112 } 113 } 114 115 // Else it is a simple molecule appearing on one side of conversion. This means 116 // it is metabolic change. 117 118 else 119 { 120 if (mineMetabolicChange) 121 { 122 SimpleInteraction mc = new SimpleInteraction(A, B, METABOLIC_CATALYSIS); 123 mc.addMediator(cont); 124 mc.addMediator(conv); 125 interactionSet.add(mc); 126 } 127 } 128 } 129 } 130 } 131 } 132 133 /** 134 * Creates a list of conversions on which this control has an effect. If the 135 * control controls another control, then it is traversed recursively to find 136 * the affected conversions. 137 * @param cont control 138 * @param convList list of affected conversions 139 * @return list of affected conversions 140 */ 141 private List<conversion> getAffectedConversions(control cont, List<conversion> convList) 142 { 143 if (convList == null) 144 { 145 convList = new ArrayList<conversion>(); 146 } 147 148 for (process prcss : cont.getCONTROLLED()) 149 { 150 if (prcss instanceof conversion) 151 { 152 convList.add((conversion) prcss); 153 } else if (prcss instanceof control) 154 { 155 getAffectedConversions((control) prcss, convList); 156 } 157 } 158 159 return convList; 160 } 161 162 /** 163 * Collects the associated physical entities of the given participant set. 164 * @param partics participants 165 * @param peSet physical entity set to collect in 166 * @return associated physical entities 167 */ 168 private Set<physicalEntity> collectEntities(Set<physicalEntityParticipant> partics, Set<physicalEntity> peSet) 169 { 170 if (peSet == null) 171 { 172 peSet = new HashSet<physicalEntity>(); 173 } 174 175 for (physicalEntityParticipant partic : partics) 176 { 177 peSet.add(partic.getPHYSICAL_ENTITY()); 178 } 179 180 return peSet; 181 } 182 183 /** 184 * Collects the associated non-complex physical entities of the given 185 * participant set. This means we are not interested in complexes, but their 186 * members, at any nesting. 187 * @param partics participants 188 * @return associated physical entities 189 */ 190 private List<physicalEntity> collectSimpleEntities(Set<physicalEntityParticipant> partics) 191 { 192 List<physicalEntity> peList = new ArrayList<physicalEntity>(); 193 194 for (physicalEntityParticipant partic : partics) 195 { 196 physicalEntity pe = partic.getPHYSICAL_ENTITY(); 197 198 if (pe instanceof complex) 199 { 200 collectSimpleMembersOfComplex(peList, (complex) pe); 201 } else 202 { 203 peList.add(pe); 204 } 205 } 206 207 return peList; 208 } 209 210 /** 211 * Recursive method for collecting simple members of the given complex in the 212 * given list. 213 * @param list where to collect 214 * @param comp complex to collect members 215 */ 216 private void collectSimpleMembersOfComplex(List<physicalEntity> list, complex comp) 217 { 218 for (physicalEntityParticipant pep : comp.getCOMPONENTS()) 219 { 220 physicalEntity pe = pep.getPHYSICAL_ENTITY(); 221 222 if (pe instanceof complex) 223 { 224 collectSimpleMembersOfComplex(list, (complex) pe); 225 } else 226 { 227 list.add(pe); 228 } 229 } 230 } 231 232 /** 233 * Sometimes an entity is both an input and output to a conversion without any state change. 234 * Normally this phenomena should be modeled using controller property of conversion. In other 235 * cases this method detects entities that goes in and out without any change. 236 * @param entity entity to check 237 * @param conv conversion that the entity is participant 238 * @return true if entity has a change in conversion 239 */ 240 private boolean entityHasAChange(physicalEntity entity, conversion conv) 241 { 242 Set<StateWrapper> leftonly = new HashSet<StateWrapper>(); 243 Set<StateWrapper> rightonly = new HashSet<StateWrapper>(); 244 Set<StateWrapper> both = new HashSet<StateWrapper>(); 245 246 for (physicalEntityParticipant pep : conv.getLEFT()) 247 { 248 if (pep.getPHYSICAL_ENTITY() == entity) 249 { 250 leftonly.add(new StateWrapper(pep)); 251 } 252 } 253 254 for (physicalEntityParticipant pep : conv.getRIGHT()) 255 { 256 if (pep.getPHYSICAL_ENTITY() == entity) 257 { 258 StateWrapper sw = new StateWrapper(pep); 259 if (leftonly.contains(sw)) 260 { 261 leftonly.remove(sw); 262 both.add(sw); 263 } else if (!both.contains(sw)) 264 { 265 rightonly.add(sw); 266 } 267 } 268 } 269 270 return !leftonly.isEmpty() || !rightonly.isEmpty(); 271 } 272 273 /** 274 * Gets supported interaction types. 275 * @return supported interaction types 276 */ 277 public List<BinaryInteractionType> getRuleTypes() 278 { 279 return binaryInteractionTypes; 280 } 281 282 /** 283 * This wrapper is used for using state equality of participants in sets. 284 */ 285 private class StateWrapper 286 { 287 /** 288 * Wrapped participant. 289 */ 290 final physicalEntityParticipant pep; 291 292 /** 293 * Constructor with the wrapped participant. 294 * @param pep wrapped participant 295 */ 296 private StateWrapper(physicalEntityParticipant pep) 297 { 298 this.pep = pep; 299 } 300 301 /** 302 * Generates hash code for the state of the participant. 303 * @return hash code 304 */ 305 public int hashCode() 306 { 307 return pep.stateCode(); 308 } 309 310 /** 311 * Checks if two participants are in equivalent state. 312 * @param obj other participant 313 * @return true if they are in equivalent state 314 */ 315 public boolean equals(Object obj) 316 { 317 if (obj instanceof StateWrapper) 318 { 319 StateWrapper sw = (StateWrapper) obj; 320 321 return pep.isInEquivalentState(sw.pep); 322 } 323 324 return false; 325 } 326 } 327}