001package org.biopax.paxtools.pattern.constraint;
002
003import org.biopax.paxtools.controller.PathAccessor;
004import org.biopax.paxtools.model.BioPAXElement;
005import org.biopax.paxtools.model.level3.PhysicalEntity;
006import org.biopax.paxtools.pattern.Match;
007import org.biopax.paxtools.pattern.util.Blacklist;
008
009import java.util.Collection;
010import java.util.HashSet;
011import java.util.Set;
012
013/**
014 * Many times we want to link PhysicalEntities (PE) while traversing a relation. For instance when
015 * we have a PE and want to go the Interactions that it participates, we may also want to consider
016 * the complexes that the PE is in. Or we also want to traverse homologies (parent or member PEs)
017 * This generative constraint gets related generics (member or parent) and either complexes or
018 * members recursively.
019 *
020 * Linking process is a bit confusing. There two modes: TO_COMPLEX and TO_MEMBER. Linking homologies
021 * is identical for both cases. IT is either parent PEs recursively, or member PEs recursively. But
022 * not parent of a member (recursion do not alternate between parent and member).
023 *
024 * In TO_COMPLEX mode, only parent complexes are linked recursively, and in TO_MEMBER mode, only the
025 * complex member PEs are linked recursively.
026 *
027 * Homology linking and complex-relation linking can alternate recursively.
028 *
029 * @author Ozgun Babur
030 */
031public class LinkedPE extends ConstraintAdapter
032{
033        /**
034         * Type of the linking.
035         */
036        private Type type;
037
038        /**
039         * Accessor to get parent PhysicalEntity.
040         */
041        private static PathAccessor upperGenAcc = new PathAccessor("PhysicalEntity/memberPhysicalEntityOf*");
042
043        /**
044         * Accessor to get member PhysicalEntity.
045         */
046        private static PathAccessor lowerGenAcc = new PathAccessor("PhysicalEntity/memberPhysicalEntity*");
047
048        /**
049         * Accessor to get parent Complex.
050         */
051        private static PathAccessor complexAcc = new PathAccessor("PhysicalEntity/componentOf*");
052
053        /**
054         * Accessor to get complex members.
055         */
056        private static PathAccessor memberAcc = new PathAccessor("Complex/component*");
057
058        /**
059         * Constructor with the linking type.
060         * @param type type of desired linking
061         */
062        public LinkedPE(Type type)
063        {
064                this(type, null);
065        }
066
067        /**
068         * Constructor with the linking type.
069         * @param type type of desired linking
070         * @param blacklist a skip-list of ubiquitous molecules
071         */
072        public LinkedPE(Type type, Blacklist blacklist)
073        {
074                super(2, blacklist);
075                this.type = type;
076        }
077
078        /**
079         * This is a generative constraint.
080         * @return true
081         */
082        @Override
083        public boolean canGenerate()
084        {
085                return true;
086        }
087
088        /**
089         * Gets to the linked PhysicalEntity.
090         * @param match current pattern match
091         * @param ind mapped indices
092         * @return linked PhysicalEntity
093         */
094        @Override
095        public Collection<BioPAXElement> generate(Match match, int... ind)
096        {
097                PhysicalEntity pe = (PhysicalEntity) match.get(ind[0]);
098                Set<BioPAXElement> set = getLinkedElements(pe);
099
100                return set;
101        }
102
103        public Set<BioPAXElement> getLinkedElements(PhysicalEntity pe)
104        {
105                Set<BioPAXElement> set = new HashSet<BioPAXElement>();
106                set.add(pe);
107                enrichWithGenerics(set, set);
108                return set;
109        }
110
111        /**
112         * Gets the linked homologies and then switches to complex-relationship mode. These two enrich
113         * methods call each other recursively.
114         * @param seed to get the linked elements from
115         * @param all already found links
116         */
117        protected void enrichWithGenerics(Set<BioPAXElement> seed, Set<BioPAXElement> all)
118        {
119                Set addition;
120                if (type == Type.TO_GENERAL) addition = access(upperGenAcc, seed, all);
121                else addition = access(lowerGenAcc, seed, all);
122                
123                all.addAll(addition);
124                seed.addAll(addition);
125
126                enrichWithCM(seed, all);
127        }
128
129        /**
130         * Gets parent complexes or complex members recursively according to the type of the linkage.
131         * @param seed elements to link
132         * @param all already found links
133         */
134        protected void enrichWithCM(Set<BioPAXElement> seed, Set<BioPAXElement> all)
135        {
136                Set addition = access(type == Type.TO_GENERAL ? complexAcc : memberAcc, seed, all);
137
138                if (blacklist != null) addition = blacklist.getNonUbiqueObjects(addition);
139
140                if (!addition.isEmpty())
141                {
142                        all.addAll(addition);
143                        enrichWithGenerics(addition, all);
144                }
145        }
146
147        /**
148         * Uses the given PathAccessor to access fields of the seed and return only new elements that is
149         * not in the given element set (all).
150         * @param pa accessor to the filed
151         * @param seed entities to get their fields
152         * @param all already found values
153         * @return new values
154         */
155        protected Set access(PathAccessor pa, Set<BioPAXElement> seed, Set<BioPAXElement> all)
156        {
157                Set set = pa.getValueFromBeans(seed);
158                set.removeAll(all);
159
160                // remove blacklisted
161                if (blacklist == null) return set;
162                else return blacklist.getNonUbiqueObjects(set);
163        }
164
165        /**
166         * Two type of linking between PhysicalEntity.
167         */
168        public enum Type
169        {
170                TO_GENERAL,
171                TO_SPECIFIC
172        }
173}