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}