001package org.biopax.paxtools.io.sif.level3; 002 003import org.biopax.paxtools.io.sif.BinaryInteractionType; 004import org.biopax.paxtools.model.BioPAXElement; 005import org.biopax.paxtools.model.Model; 006import org.biopax.paxtools.model.level3.*; 007import org.biopax.paxtools.model.level3.Process; 008import org.biopax.paxtools.util.ClassFilterSet; 009import org.biopax.paxtools.util.IllegalBioPAXArgumentException; 010 011import java.util.Arrays; 012import java.util.HashSet; 013import java.util.List; 014import java.util.Set; 015 016import static org.biopax.paxtools.io.sif.BinaryInteractionType.SEQUENTIAL_CATALYSIS; 017 018/** 019 * This class creates an interaction between two entities if they are catalyzing consecutive 020 * conversions. Conversions are considered consecutive if one of the RIGHT participants of one 021 * reaction is the LEFT of the other and if the directions of catalysis and control matches. 022 * 023 * @author Emek Demir 024 */ 025public class ConsecutiveCatalysisRule extends InteractionRuleL3Adaptor 026{ 027 /** 028 * Supported interaction types. 029 */ 030 private static List<BinaryInteractionType> binaryInteractionTypes = 031 Arrays.asList(SEQUENTIAL_CATALYSIS); 032 033 /** 034 * Infers interactions using the given PhysicalEntity as seed. 035 * @param interactionSet to be populated 036 * @param pe PhysicalEntity that will be the seed of the inference 037 * @param model BioPAX model 038 */ 039 public void inferInteractionsFromPE(InteractionSetL3 interactionSet, PhysicalEntity pe, 040 Model model) 041 { 042 for (Interaction inter : pe.getParticipantOf()) { 043 if (inter instanceof Catalysis) { 044 BioPAXElement bpe = interactionSet.getGroupMap().getEntityReferenceOrGroup(pe); 045 processCatalysis(interactionSet, (Catalysis) inter, bpe); 046 } 047 } 048 } 049 050 /** 051 * Continues the inference from the Catalysis of seed element. 052 * @param interactionSet inferred interactions 053 * @param aCatalysis the catalysis to follow 054 * @param bpe source of the interaction 055 */ 056 private void processCatalysis(InteractionSetL3 interactionSet, Catalysis aCatalysis, 057 BioPAXElement bpe) 058 { 059 //We have to consider two direction statements 060 //Catalysis.direction and Conversion.spontaneous 061 //This method maps the former to the compatible latter 062 //null means reversible or unknown, both are treated in the same way. 063 ConversionDirectionType catalysisDirection = mapDirectionToConversion(aCatalysis.getCatalysisDirection()); 064 065 //get the conversions and process them. 066 Set<Process> controlled = aCatalysis.getControlled(); 067 for (Process process : controlled) { 068 assert process instanceof Conversion; 069 070 Conversion aConversion = (Conversion) process; 071 072 //let's find the direction that is compatible with catalysis direction 073 ConversionDirectionType dirA = 074 findConsensusDirection(catalysisDirection, aConversion.getConversionDirection()); 075 076 assert dirA != null; 077 078 //and let's get the interacting physical entities 079 createInteractions(aConversion, dirA, aCatalysis, interactionSet, bpe); 080 } 081 } 082 083 /** 084 * This method will find the compatible conversion and catalysis direction. 085 * 086 * @param direction1 type implied by catalysis 087 * @param direction2 type of the conversion 088 * @return compatible direction 089 */ 090 private ConversionDirectionType findConsensusDirection(ConversionDirectionType direction1, 091 ConversionDirectionType direction2) 092 { 093 ConversionDirectionType consensus; 094 boolean first = isReversible(direction1); 095 boolean second = isReversible(direction2); 096 097 // If any one of them is not-spontaneous than consensus is the other direction. 098 // If both of them are spontaneous, then consensus is null if they are spontenous 099 // in opposite directions. 100 if (first) { 101 if (second) { 102 consensus = ConversionDirectionType.REVERSIBLE; 103 } else { 104 consensus = direction2; 105 } 106 } else { 107 if (second) { 108 consensus = direction1; 109 } else { 110 consensus = direction1.equals(direction2) ? direction1 : null; 111 } 112 } 113 return consensus; 114 } 115 116 /** 117 * Checks if the direction can be treated as reversible. 118 * @param direction1 direction to check 119 * @return true if direction is REVERSIBLE or null, false otherwise 120 */ 121 private boolean isReversible(ConversionDirectionType direction1) { 122 return direction1 == null || direction1.equals(ConversionDirectionType.REVERSIBLE); 123 } 124 125 /** 126 * Continues inference with the current elements. 127 * @param centerConversion first Conversion 128 * @param dirA current direction fot the Conversion 129 * @param aCatalysis the traversed catalysis 130 * @param interactionSet inferred interactions 131 * @param bpe source of interaction 132 */ 133 private void createInteractions(Conversion centerConversion, ConversionDirectionType dirA, 134 Catalysis aCatalysis, InteractionSetL3 interactionSet, BioPAXElement bpe) 135 { 136 //get the pes at the correct side of the conversion. 137 Set<PhysicalEntity> pes = getOutputPEs(dirA, centerConversion); 138 139 for (PhysicalEntity pe : pes) { 140 for (Interaction inter : pe.getParticipantOf()) { 141 if (inter instanceof Conversion) { 142 Conversion nextConversion = (Conversion) inter; 143 144 if (centerConversion == nextConversion) { 145 continue; 146 } 147 148 Set<Control> controls = nextConversion.getControlledOf(); 149 150 for (Catalysis consequentCatalysis : new ClassFilterSet<Control, Catalysis>( 151 controls, Catalysis.class)) 152 { 153 ConversionDirectionType direction2 = 154 mapDirectionToConversion(consequentCatalysis.getCatalysisDirection()); 155 ConversionDirectionType dirB = 156 findConsensusDirection(nextConversion.getConversionDirection(), direction2); 157 158 // Ensure intermediate molecule (pe) is input to the neighbor conversion 159 if (commonSubstrateFollowsFlow(pe, nextConversion, dirB)) { 160 for (Controller controller : consequentCatalysis.getController()) { 161 if (controller instanceof PhysicalEntity) { 162 createAndAdd(bpe, interactionSet.getGroupMap().getEntityReferenceOrGroup(controller), 163 interactionSet, BinaryInteractionType.SEQUENTIAL_CATALYSIS, 164 aCatalysis, consequentCatalysis, pe); 165 } 166 } 167 } 168 } 169 } 170 } 171 } 172 } 173 174 /** 175 * Checks if the common substrate is an input to one and output of the other. 176 * @param pe common substrate 177 * @param nextConversion second conversion 178 * @param dirB direction for the second conversion 179 * @return true if the flow matches 180 */ 181 private boolean commonSubstrateFollowsFlow(PhysicalEntity pe, Conversion nextConversion, 182 ConversionDirectionType dirB) 183 { 184 if (dirB == null) return true; //Assume reversible 185 switch (dirB) { 186 case REVERSIBLE: 187 return true; 188 case LEFT_TO_RIGHT: 189 return nextConversion.getLeft().contains(pe); 190 case RIGHT_TO_LEFT: 191 return nextConversion.getRight().contains(pe); 192 default: 193 throw new IllegalBioPAXArgumentException(); // Should never hit here 194 } 195 } 196 197 198 199 /** 200 * This method returns the PEPs that are on the correct side of the conversion 201 * 202 * @param direction determining the side 203 * @param aConversion Conversion to get inputs or outputs 204 * @return inputs or outputs 205 */ 206 private Set<PhysicalEntity> getOutputPEs(ConversionDirectionType direction, Conversion aConversion) { 207 switch (direction) { 208 case LEFT_TO_RIGHT: 209 return aConversion.getRight(); 210 case RIGHT_TO_LEFT: 211 return aConversion.getLeft(); 212 default: 213 return mergedSet(aConversion); 214 } 215 } 216 217 /** 218 * Gets all left and right participant of a conversion in a set. 219 * @param aConversion Conversion to get participants 220 * @return left and right participants of the conversion 221 */ 222 private HashSet<PhysicalEntity> mergedSet(Conversion aConversion) { 223 HashSet<PhysicalEntity> hashSet = new HashSet<PhysicalEntity>(); 224 hashSet.addAll(aConversion.getLeft()); 225 hashSet.addAll(aConversion.getRight()); 226 return hashSet; 227 } 228 229 /** 230 * Converts catalysis direction to the conversion direction. 231 * @param direction catalysis direction to convert 232 * @return conversion direction 233 */ 234 private ConversionDirectionType mapDirectionToConversion(CatalysisDirectionType direction) { 235 if (direction != null) { 236 switch (direction) { 237 case LEFT_TO_RIGHT: 238 return ConversionDirectionType.LEFT_TO_RIGHT; 239 case RIGHT_TO_LEFT: 240 return ConversionDirectionType.RIGHT_TO_LEFT; 241 } 242 } 243 return null; 244 } 245 246 /** 247 * Gets supported interaction types. 248 * @return supported interaction types 249 */ 250 public List<BinaryInteractionType> getRuleTypes() { 251 return binaryInteractionTypes; 252 } 253}