001package org.biopax.paxtools.pattern.util;
002
003import org.biopax.paxtools.controller.PathAccessor;
004import org.biopax.paxtools.model.level3.*;
005
006import java.util.HashSet;
007import java.util.Set;
008
009/**
010 * This class takes two PhysicalEntity objects linked with generic or complex member relationships,
011 * and prepares an array of PEs that link those.
012 * 
013 * @author Ozgun Babur
014 */
015public class PhysicalEntityChain
016{
017        /**
018         * Array that links two ends of the chain.
019         */
020        public PhysicalEntity[] pes;
021
022        /**
023         * Accessor to the modification term.
024         */
025        protected static final PathAccessor PE2TERM =
026                new PathAccessor("PhysicalEntity/feature:ModificationFeature/modificationType/term");
027
028        /**
029         * Accessor to the modification term.
030         */
031        protected static final PathAccessor PE2FEAT =
032                new PathAccessor("PhysicalEntity/feature:ModificationFeature");
033
034        /**
035         * Constructor with endpoints. The end points are differentiated with the terms small and big.
036         * This refers to a hierarchy in the relation. Small means you will get to this end while going
037         * towards MEMBER direction, and big means this end is at the COMPLEX direction. To understand
038         * this please see the directions in LinkedPE.
039         * @param small member end of the chain
040         * @param big complex end of the chain
041         * @see org.biopax.paxtools.pattern.constraint.LinkedPE
042         */
043        public PhysicalEntityChain(PhysicalEntity small, PhysicalEntity big)
044        {
045                pes = fillArray(big, small, 1, 0);
046                
047                if (pes == null)
048                {
049                        throw new IllegalArgumentException("No link found between small PE = " +
050                                small.getRDFId() + " and big PE = " + big.getRDFId());
051                }
052                assert !containsNull(pes);
053        }
054
055        /**
056         * Checks if any element in the chain is null.
057         * @param pes element array
058         * @return true if null found
059         */
060        private boolean containsNull(PhysicalEntity[] pes)
061        {
062                for (PhysicalEntity pe : pes)
063                {
064                        if (pe == null) return true;
065                }
066                return false;
067        }
068
069        /**
070         * Creates the chain that links the given endpoints.
071         * @param parent current element
072         * @param target target at the member end
073         * @param depth current depth
074         * @param dir current direction to traverse homologies
075         * @return array of entities
076         */
077        protected PhysicalEntity[] fillArray(PhysicalEntity parent, PhysicalEntity target, int depth,
078                int dir)
079        {
080                if (parent == target)
081                {
082                        PhysicalEntity[] pes = new PhysicalEntity[depth];
083                        pes[0] = target;
084                        return pes;
085                }
086                
087                if (parent instanceof Complex)
088                {
089                        for (PhysicalEntity mem : ((Complex) parent).getComponent())
090                        {
091                                PhysicalEntity[] pes = fillArray(mem, target, depth + 1, 0);
092                                if (pes != null)
093                                {
094                                        pes[pes.length - depth] = parent;
095                                        return pes;
096                                }
097                        }
098                }
099
100                if (dir <= 0)
101                for (PhysicalEntity mem : parent.getMemberPhysicalEntity())
102                {
103                        PhysicalEntity[] pes = fillArray(mem, target, depth + 1, -1);
104                        if (pes != null)
105                        {
106                                pes[pes.length - depth] = parent;
107                                return pes;
108                        }
109                }
110
111                if (dir >= 0)
112                for (PhysicalEntity grand : parent.getMemberPhysicalEntityOf())
113                {
114                        PhysicalEntity[] pes = fillArray(grand, target, depth + 1, 1);
115                        if (pes != null)
116                        {
117                                pes[pes.length - depth] = parent;
118                                return pes;
119                        }
120                }
121
122                return null;
123        }
124
125        /**
126         * Retrieves the cellular location of the PhysicalEntity.
127         * @return cellular location of the PhysicalEntity
128         */
129        public Set<String> getCellularLocations()
130        {
131                Set<String> locs = new HashSet<String>();
132                for (PhysicalEntity pe : pes)
133                {
134                        CellularLocationVocabulary voc = pe.getCellularLocation();
135                        if (voc != null)
136                        {
137                                for (String term : voc.getTerm())
138                                {
139                                        if (term != null && term.length() > 0) locs.add(term);
140                                }
141                        }
142                }
143                return locs;
144        }
145
146        /**
147         * Checks if two chains intersect without ignoring endpoint intersection.
148         * @param rpeh second chain
149         * @return true if they intersect
150         */
151        public boolean intersects(PhysicalEntityChain rpeh)
152        {
153                return intersects(rpeh, false);
154        }
155
156        /**
157         * Checks if two chains intersect.
158         * @param rpeh second chain
159         * @param ignoreEndPoints flag to ignore intersections at the endpoints of the chains
160         * @return true if they intersect
161         */
162        public boolean intersects(PhysicalEntityChain rpeh, boolean ignoreEndPoints)
163        {
164                for (PhysicalEntity pe1 : pes)
165                {
166                        for (PhysicalEntity pe2 : rpeh.pes)
167                        {
168                                if (pe1 == pe2)
169                                {
170                                        if (ignoreEndPoints)
171                                        {
172                                                if ((pes[0] == pe1 || pes[pes.length-1] == pe1) &&
173                                                        (rpeh.pes[0] == pe2 || rpeh.pes[rpeh.pes.length-1] == pe2))
174                                                {
175                                                        continue;
176                                                }
177                                        }
178
179                                        return true;
180                                }
181                        }
182                }
183                return false;
184        }
185
186        /**
187         * Checks if the chain has a member with an activity label.
188         * @return the activity status found
189         */
190        public Activity checkActivityLabel()
191        {
192                boolean active = false;
193                boolean inactive = false;
194
195                for (PhysicalEntity pe : pes)
196                {
197                        for (Object o : PE2TERM.getValueFromBean(pe))
198                        {
199                                String s = (String) o;
200                                if (s.contains("inactiv")) inactive = true;
201                                else if (s.contains("activ")) active = true;
202                        }
203
204                        for (String s : pe.getName())
205                        {
206                                if (s.contains("inactiv")) inactive = true;
207                                else if (s.contains("activ")) active = true;
208                        }
209                }
210
211                if (active) if (inactive) return Activity.BOTH; else return Activity.ACTIVE;
212                else if (inactive) return Activity.INACTIVE; else return Activity.NONE;
213        }
214
215        /**
216         * Values for activity.
217         */
218        public enum Activity
219        {
220                ACTIVE,
221                INACTIVE,
222                BOTH,
223                NONE
224        }
225
226        /**
227         * Collects modifications from the elements of the chain.
228         * @return modifications
229         */
230        public Set<ModificationFeature> getModifications()
231        {
232                Set<ModificationFeature> set = new HashSet<ModificationFeature>();
233
234                for (PhysicalEntity pe : pes)
235                {
236                        set.addAll(PE2FEAT.getValueFromBean(pe));
237                }
238                return set;
239        }
240}