001package org.biopax.paxtools.query.algorithm;
002
003import org.biopax.paxtools.query.model.GraphObject;
004import org.biopax.paxtools.query.model.Node;
005
006import java.util.*;
007
008/**
009 * Searches common downstream or common upstream of a specified set of entities
010 * based on the given direction within the boundaries of a specified length
011 * limit. Takes a source set of entities, direction of the query and
012 * distance limit.
013 *
014 * @author Ozgun Babur
015 * @author Merve Cakir
016 * @author Shatlyk Ashyralyev
017 */
018public class CommonStreamQuery
019{
020        /**
021         * Collection of Set of nodes.
022         * Each Set contains all states of corresponding physical entity or
023         * contains one of the selected nodes
024         */
025        private Collection<Set<Node>> sourceSet;
026        
027        /**
028         * The direction to determine whether the search will be to look for common
029         * downstream or common upstream.
030         */
031        private Direction direction;
032
033        /**
034         * Stop distance.
035         */
036        private int limit;
037
038        /**
039         * The map to hold the reached counts of graph objects. Reached count
040         * represents whether the particular graph object is in the boundaries
041         * of BFS.
042         */
043        Map<GraphObject, Integer> reachedCount = new HashMap<GraphObject, Integer>();
044
045        /**
046         * Constructor for Common Stream with Selected Nodes.
047         * @param sourceNodeSet Source nodes
048         * @param direction Common upstream or downstream. Cannot be bothstream
049         * @param limit Search length limit
050         */
051        public CommonStreamQuery(Set<Node> sourceNodeSet, Direction direction, int limit)
052        {
053                if (direction != Direction.UPSTREAM && direction != Direction.DOWNSTREAM)
054                        throw new IllegalArgumentException("Direction has to be either upstream or downstream");
055
056                this.sourceSet = new LinkedHashSet<Set<Node>>();
057                
058                //Each set contains only one selected Node
059                for (Node node : sourceNodeSet)
060                {
061                        Set<Node> sourceNode = new HashSet<Node>();
062                        sourceNode.add(node);
063                        sourceSet.add(sourceNode);
064                }
065                
066                this.direction = direction;
067                this.limit = limit;
068        }  
069        
070        /**
071         * Constructor for Common Stream with Entity States.
072         * @param sourceStateSet Collection of source node sets
073         * @param direction Common upstream or downstream. Cannot be bothstream
074         * @param limit Search length limit
075         */
076        public CommonStreamQuery(Collection<Set<Node>> sourceStateSet, Direction direction, int limit)
077        {
078                if (direction != Direction.UPSTREAM && direction != Direction.DOWNSTREAM)
079                        throw new IllegalArgumentException("Direction has to be either upstream or downstream");
080
081                this.sourceSet = sourceStateSet;
082                this.direction = direction;
083                this.limit = limit;
084        }
085
086        /**
087         * Method to run the query.
088         * @return Common stream
089         */
090        public Set<GraphObject> run()
091        {
092                /**
093                 * Candidate contains all the graph objects that are the results of BFS.
094                 * Eliminating nodes from candidate according to the reached counts
095                 * will yield result.
096                 */
097                Map<GraphObject, Integer> candidate = new HashMap<GraphObject, Integer>();
098                Set<GraphObject> result = new HashSet<GraphObject>();
099                
100                //for each set of states of entity, run BFS separately
101                for (Set<Node> source : sourceSet)
102                {
103                        //run BFS for set of states of each entity
104                        BFS bfs = new BFS (source, null, direction, limit);
105                        Map<GraphObject, Integer> BFSResult = new HashMap<GraphObject, Integer>();
106                        BFSResult.putAll(bfs.run());
107
108                        /**
109                         * Reached counts of the graph objects that are in BFSResult will
110                         * be incremented by 1.
111                         */
112                        for (GraphObject go : BFSResult.keySet())
113                        {
114                                setLabel(go, (getLabel(go) + 1));
115                        }
116                        
117                        //put BFS Result into candidate set
118                        candidate.putAll(BFSResult);
119                }
120                        
121                /**
122                 * Having a reached count equal to number of nodes in the source set
123                 * indicates being in common stream. 
124                 */
125                for(GraphObject go : candidate.keySet())
126                {
127                        if (getLabel(go) == sourceSet.size())
128                        {
129                                result.add(go);
130                        }
131                }
132
133                //Return the result of query
134                return result;
135        }
136        
137        /**
138         * Method for getting Label of GraphObject.
139         * If Label is absent, then it returns 0.
140         * @param go Graph object to get its label
141         * @return Label of the graph object
142         */
143        private int getLabel(GraphObject go)
144        {
145                if (!reachedCount.containsKey(go))
146                {
147                        // Absence of label is interpreted as zero
148                        return 0;
149                }
150                else
151                {
152                        return reachedCount.get(go);
153                }
154        }
155
156        /**
157         * Method for setting the Label of GraphObject
158         * @param go Graph object to set its label
159         * @param label New label of the graph object
160         */
161        private void setLabel(GraphObject go, int label)
162        {
163                reachedCount.put(go, label);
164        }
165}