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.*;
008import org.biopax.paxtools.util.ClassFilterSet;
009
010import java.util.*;
011
012import static org.biopax.paxtools.io.sif.BinaryInteractionType.SEQUENTIAL_CATALYSIS;
013
014/**
015 * This class creates an interaction between two entities if they are catalyzing
016 * consecutive conversions. Conversions are considered consecutive if one of the
017 * RIGHT participants of one reaction is the LEFT of the other and if the
018 * directions of catalysis and control matches. User: demir Date: Dec 28, 2007
019 * Time: 10:40:01 PM
020 */
021public class ConsecutiveCatalysisRule extends InteractionRuleL2Adaptor
022{
023        /**
024         * Supported interaction types.
025         */
026        private static List<BinaryInteractionType> binaryInteractionTypes =
027                Arrays.asList(SEQUENTIAL_CATALYSIS);
028
029        /**
030         * Infers using the given physicalEntity as source.
031         * @param interactionSet to be populated
032         * @param pe source of the interaction
033         * @param model BioPAX model
034         */
035        public void inferInteractionsFromPE(InteractionSet interactionSet, physicalEntity pe, Model model)
036        {
037                Set<catalysis> catalyses = pe.getAllInteractions(catalysis.class);
038                for (catalysis aCatalysis : catalyses)
039                {
040                        processCatalysis(interactionSet, pe, aCatalysis);
041                }
042        }
043
044        /**
045         * Continues inference through the given catalysis.
046         * @param interactionSet to populate
047         * @param pe source
048         * @param aCatalysis catalysis to process
049         */
050        private void processCatalysis(InteractionSet interactionSet, physicalEntity pe, catalysis aCatalysis)
051        {
052                //We have to consider two direction statements
053                //Catalysis.direction and Conversion.spontaneous
054                //This method maps the former to the compatible latter
055                //null means reversible or unknown, both are treated in the same way.
056                SpontaneousType catalysisDirection = mapDirectionToSpontaneous(aCatalysis.getDIRECTION());
057                //get the conversions and process them.
058                Set<process> controlled = aCatalysis.getCONTROLLED();
059                for (process process : controlled)
060                {
061                        conversion aConversion = (conversion) process;
062                        //let's find the direction that is compatible with catalysis direction
063                        SpontaneousType direction = findConsensusDirection(catalysisDirection, aConversion.getSPONTANEOUS());
064                        assert direction != null;
065                        //and let's get the interacting physical entities
066                        createInteractions(aConversion, direction, pe, aCatalysis, interactionSet);
067                }
068        }
069
070        /**
071         * This method finds the compatible conversion and catalysis direction.
072         * @param direction1 type implied by catalysis
073         * @param direction2 type of the conversion
074         * @return consensus direction
075         */
076        private SpontaneousType findConsensusDirection(SpontaneousType direction1, SpontaneousType direction2)
077        {
078                SpontaneousType consensus;
079                boolean first = isReversible(direction1);
080                boolean second = isReversible(direction2);
081                //      If any one of them is not-spontaneous than consensus is the other direction.
082                //  If both of them are spontaneous, then consensus is null if they are spontenous
083                //  in opposite directions.
084                if (first)
085                {
086                        if (second)
087                        {
088                                consensus = SpontaneousType.NOT_SPONTANEOUS;
089                        } else
090                        {
091                                consensus = direction2;
092                        }
093                } else
094                {
095                        if (second)
096                        {
097                                consensus = direction1;
098                        } else
099                        {
100                                consensus = direction1.equals(direction2) ? direction1 : null;
101                        }
102                }
103                return consensus;
104        }
105
106        /**
107         * Checks if the direction can be treated as reversible.
108         * @param direction1 direction to check
109         * @return true if reversible or null
110         */
111        private boolean isReversible(SpontaneousType direction1)
112        {
113                return direction1 == null || direction1.equals(SpontaneousType.NOT_SPONTANEOUS);
114        }
115
116        /**
117         * Continues inference with the given conversion, catalysis and direction.
118         * @param centerConversion first conversion
119         * @param direction direction of the center conversion traversed
120         * @param pe source of interaction
121         * @param aCatalysis catalysis where source is the controller
122         * @param interactionSet
123         */
124        private void createInteractions(conversion centerConversion, SpontaneousType direction,
125                physicalEntity pe, catalysis aCatalysis, InteractionSet interactionSet)
126        {
127                //get the peps at the correct side of the conversion.
128                Set<physicalEntityParticipant> peps = getCompatiblePEPs(direction, centerConversion);
129                //for these set of peps find compatible conversions
130                Set<conversion> conversions = getCompatibleConversions(peps, direction);
131
132                // for each conversion find pes that catalyze them and add an interaction
133                for (conversion neighbor : conversions)
134                {
135                        findAndAddCatalysts(neighbor, direction, pe, aCatalysis, interactionSet);
136                }
137        }
138
139        /**
140         * This method returns the PEPs that are on the correct side of the conversion.
141         * @param direction determining the side
142         * @param aConversion conversion to get PEPs
143         * @return PEPs that are at the desired side
144         */
145        private Set<physicalEntityParticipant> getCompatiblePEPs(SpontaneousType direction, conversion aConversion)
146        {
147                switch (direction)
148                {
149                        case L_R:
150                                return aConversion.getRIGHT();
151                        case R_L:
152                                return aConversion.getLEFT();
153                        default:
154                                return mergedSet(aConversion);
155                }
156        }
157
158        /**
159         * Gets left and right participants of the conversion.
160         * @param aConversion conversion to get left and right participants
161         * @return left and right participants of the conversion
162         */
163        private HashSet<physicalEntityParticipant> mergedSet(conversion aConversion)
164        {
165                HashSet<physicalEntityParticipant> hashSet = new HashSet<physicalEntityParticipant>();
166                hashSet.addAll(aConversion.getLEFT());
167                hashSet.addAll(aConversion.getRIGHT());
168                return hashSet;
169        }
170
171        /**
172         * Gets the second conversions compatible with the current elements already traversed.
173         * @param peps PEP that are input or output to the conversion
174         * @param direction traversed direction of the first conversion
175         * @return compatible conversions
176         */
177        private Set<conversion> getCompatibleConversions(Set<physicalEntityParticipant> peps, SpontaneousType direction)
178        {
179                Set<conversion> compatibleConversions = new HashSet<conversion>();
180                for (physicalEntityParticipant pep : peps)
181                {
182                        Set<physicalEntityParticipant> npeps = pep.getPHYSICAL_ENTITY().isPHYSICAL_ENTITYof();
183                        for (physicalEntityParticipant npep : npeps)
184                        {
185                                if (!pep.equals(npep) && pep.isInEquivalentState(npep))
186                                {
187                                        if (!npep.isPARTICIPANTSof().isEmpty())
188                                        {
189                                                assert npep.isPARTICIPANTSof().size() == 1;
190                                                interaction anI = npep.isPARTICIPANTSof().iterator().next();
191
192                                                if (anI instanceof conversion)
193                                                {
194                                                        conversion aConversion = (conversion) anI;
195
196                                                        if (findConsensusDirection(direction, aConversion.getSPONTANEOUS()) != null &&
197                                                            participantIsAtACompatibleSide(direction, npep, aConversion))
198                                                        {
199                                                                compatibleConversions.add(aConversion);
200                                                        }
201                                                }
202                                        }
203                                }
204                        }
205                }
206                return compatibleConversions;
207        }
208
209        /**
210         * Checks if the placement of participant is compatible with the traversal direction.
211         * @param direction direction traversed
212         * @param npep participant to check
213         * @param aConversion the conversion
214         * @return true if the participant is at a compatible side
215         */
216        private boolean participantIsAtACompatibleSide(SpontaneousType direction,
217                physicalEntityParticipant npep, conversion aConversion)
218        {
219                switch (direction)
220                {
221                        case L_R:
222                                return aConversion.getLEFT().contains(npep);
223                        case R_L:
224                                return aConversion.getRIGHT().contains(npep);
225                        default:
226                                return aConversion.getRIGHT().contains(npep) || aConversion.getLEFT().contains(npep);
227                }
228        }
229
230        /**
231         * Creates interactions with the catalysis of the second conversion.
232         * @param aConversion second conversion
233         * @param direction direction of the second conversion traversed
234         * @param pe source
235         * @param aCatalysis catalysis of the second conversion
236         * @param interactionSet to populate
237         */
238        private void findAndAddCatalysts(conversion aConversion, SpontaneousType direction,
239                physicalEntity pe, catalysis aCatalysis, InteractionSet interactionSet)
240        {
241                Set<control> controls = aConversion.isCONTROLLEDOf();
242                for (catalysis consequentCatalysis : new ClassFilterSet<control, catalysis>(controls, catalysis.class))
243                {
244                        if (findConsensusDirection(direction, mapDirectionToSpontaneous(consequentCatalysis.getDIRECTION())) !=
245                            null)
246                        {
247                                for (physicalEntityParticipant pepi : consequentCatalysis.getCONTROLLER())
248                                {
249                                        //create interactions and add to set
250                                        SimpleInteraction si = new SimpleInteraction(pe, pepi.getPHYSICAL_ENTITY(), SEQUENTIAL_CATALYSIS);
251                                        si.addMediator(aCatalysis);
252                                        si.addMediator(consequentCatalysis);
253                                        interactionSet.add(si);
254                                }
255                        }
256                }
257        }
258
259        /**
260         * Converts directions.
261         * @param direction direction to convert
262         * @return equivalent direction
263         */
264        private SpontaneousType mapDirectionToSpontaneous(Direction direction)
265        {
266                if (direction != null)
267                {
268                        switch (direction)
269                        {
270                                case IRREVERSIBLE_LEFT_TO_RIGHT:
271                                case PHYSIOL_LEFT_TO_RIGHT:
272                                        return SpontaneousType.L_R;
273                                case IRREVERSIBLE_RIGHT_TO_LEFT:
274                                case PHYSIOL_RIGHT_TO_LEFT:
275                                        return SpontaneousType.R_L;
276                        }
277                }
278                return null;
279        }
280
281        /**
282         * Gets supported interaction types.
283         * @return supported interaction types
284         */
285        public List<BinaryInteractionType> getRuleTypes()
286        {
287                return binaryInteractionTypes;
288        }
289}