001package org.biopax.paxtools.pattern.constraint; 002 003import org.biopax.paxtools.model.level3.*; 004import org.biopax.paxtools.model.level3.Process; 005import org.biopax.paxtools.pattern.Constraint; 006import org.biopax.paxtools.pattern.Match; 007import org.biopax.paxtools.model.BioPAXElement; 008import org.biopax.paxtools.pattern.util.Blacklist; 009import org.biopax.paxtools.pattern.util.RelType; 010 011import java.util.*; 012 013/** 014 * This is a base class for a Constraint. Most constraints should typically extend this class. 015 * 016 * @author Ozgun Babur 017 */ 018public abstract class ConstraintAdapter implements Constraint 019{ 020 /** 021 * Size of the constraint. This is defined by how many elements this constraint needs to be \ 022 * mapped to work. 023 */ 024 protected int size; 025 026 /** 027 * Blacklist to detect ubiquitous small molecules. 028 */ 029 protected Blacklist blacklist; 030 031 /** 032 * Constructor with size. 033 * @param size size if the constraint. 034 */ 035 protected ConstraintAdapter(int size) 036 { 037 this.size = size; 038 } 039 040 /** 041 * Constructor with size. 042 * @param size size if the constraint. 043 * @param blacklist for detecting ubiquitous small molecules 044 */ 045 protected ConstraintAdapter(int size, Blacklist blacklist) 046 { 047 this.size = size; 048 this.blacklist = blacklist; 049 } 050 051 /** 052 * Empty constructor. Note that specifying the size is not mandatory since the child constraint 053 * can override <code>getVariableSize</code> instead of using the size variable. 054 */ 055 protected ConstraintAdapter() 056 { 057 } 058 059 /** 060 * Specifies if the constraint is generative. If you override this method, then don't forget to 061 * also override getGeneratedInd, and generate methods. 062 */ 063 @Override 064 public boolean canGenerate() 065 { 066 return false; 067 } 068 069 /** 070 * This method has to be overridden by generative constraints. 071 * 072 * @param match current pattern match 073 * @param ind mapped indices 074 * @return elements that satisfy this constraint 075 */ 076 @Override 077 public Collection<BioPAXElement> generate(Match match, int ... ind) 078 { 079 throw new RuntimeException("This constraint is not generative. " + 080 "Please check with canGenerate first."); 081 } 082 083 /** 084 * Use this method only if constraint canGenerate, and satisfaction criteria is that simple. 085 * 086 * @param match current pattern match 087 * @param ind mapped indices 088 * @return true if the match satisfies this constraint 089 */ 090 @Override 091 public boolean satisfies(Match match, int... ind) 092 { 093 return generate(match, ind).contains(match.get(ind[ind.length - 1])); 094 } 095 096 /** 097 * Asserts the size of teh parameter array is equal to the variable size. 098 * @param ind index array to assert its size 099 */ 100 protected void assertIndLength(int[] ind) 101 { 102 assert ind.length == getVariableSize(); 103 } 104 105 /** 106 * Sets the size of the constraint. 107 * @param size size of the constraint 108 */ 109 public void setSize(int size) 110 { 111 this.size = size; 112 } 113 114 /** 115 * Gets the variable size of the constraint. 116 * @return variable size 117 */ 118 @Override 119 public int getVariableSize() 120 { 121 return size; 122 } 123 124 //----- Section: Common BioPAX Operations -----------------------------------------------------| 125 126 /** 127 * Gets the direction of the Control chain the the Interaction. 128 * @param conv controlled conversion 129 * @param cont top control 130 * @return the direction of the conversion related to the catalysis 131 */ 132 protected ConversionDirectionType getDirection(Conversion conv, Control cont) 133 { 134 return getDirection(conv, null, cont); 135 } 136 137 /** 138 * Gets the direction of the Control chain the the Interaction. 139 * @param conv controlled conversion 140 * @param pathway the container pathway 141 * @param cont top control 142 * @return the direction of the conversion related to the catalysis 143 */ 144 protected ConversionDirectionType getDirection(Conversion conv, Pathway pathway, Control cont) 145 { 146 for (Control ctrl : getControlChain(cont, conv)) 147 { 148 ConversionDirectionType dir = getCatalysisDirection(ctrl); 149 if (dir != null) return dir; 150 } 151 152 Set<StepDirection> dirs = new HashSet<StepDirection>(); 153 154 Set<PathwayStep> convSteps = conv.getStepProcessOf(); 155 156 // maybe the direction is embedded in a pathway step 157 for (PathwayStep step : cont.getStepProcessOf()) 158 { 159 if (pathway != null && !step.getPathwayOrderOf().equals(pathway)) continue; 160 161 if (step instanceof BiochemicalPathwayStep && convSteps.contains(step)) 162 { 163 StepDirection dir = ((BiochemicalPathwayStep) step).getStepDirection(); 164 if (dir != null) dirs.add(dir); 165 } 166 } 167 168 if (dirs.size() > 1) return ConversionDirectionType.REVERSIBLE; 169 else if (!dirs.isEmpty()) return convertStepDirection(dirs.iterator().next()); 170 171 return getDirection(conv); 172 } 173 174 /** 175 * Gets the direction of the Control chain the the Interaction. 176 * @param conv controlled conversion 177 * @param pathway the container pathway 178 * @return the direction of the conversion related to the catalysis 179 */ 180 protected ConversionDirectionType getDirection(Conversion conv, Pathway pathway) 181 { 182 Set<StepDirection> dirs = new HashSet<StepDirection>(); 183 184 // find the direction in the pathway step 185 for (PathwayStep step : conv.getStepProcessOf()) 186 { 187 if (step.getPathwayOrderOf().equals(pathway) && step instanceof BiochemicalPathwayStep) 188 { 189 StepDirection dir = ((BiochemicalPathwayStep) step).getStepDirection(); 190 if (dir != null) dirs.add(dir); 191 } 192 } 193 194 if (dirs.size() > 1) return ConversionDirectionType.REVERSIBLE; 195 else if (!dirs.isEmpty()) return convertStepDirection(dirs.iterator().next()); 196 197 return getDirection(conv); 198 } 199 200 /** 201 * Gets the direction of the Control, if exists. 202 * @param cont Control to get its direction 203 * @return the direction of the Control 204 */ 205 protected ConversionDirectionType getCatalysisDirection(Control cont) 206 { 207 if (cont instanceof Catalysis) 208 { 209 CatalysisDirectionType catDir = ((Catalysis) cont).getCatalysisDirection(); 210 211 if (catDir == CatalysisDirectionType.LEFT_TO_RIGHT) 212 { 213 return ConversionDirectionType.LEFT_TO_RIGHT; 214 } 215 else if (catDir == CatalysisDirectionType.RIGHT_TO_LEFT) 216 { 217 return ConversionDirectionType.RIGHT_TO_LEFT; 218 } 219 } 220 return null; 221 } 222 223 /** 224 * Gets the chain of Control, staring from the given Control, leading to the given Interaction. 225 * Use this method only if you are sure that there is a link from the control to conversion. 226 * Otherwise a RuntimeException is thrown. This assumes that there is only one control chain 227 * towards the interaction. It not, then one of the chains will be returned. 228 * 229 * @param control top level Control 230 * @param inter target Interaction 231 * @return Control chain controlling the Interaction 232 */ 233 protected List<Control> getControlChain(Control control, Interaction inter) 234 { 235 LinkedList<Control> list = new LinkedList<Control>(); 236 list.add(control); 237 238 boolean found = search(list, inter); 239 240 if (!found) throw new RuntimeException("No link from Control to Conversion."); 241 242 return list; 243 } 244 245 /** 246 * Checks if the control chain is actually controlling the Interaction. 247 * @param list the Control chain 248 * @param inter target Interaction 249 * @return true if the chain controls the Interaction 250 */ 251 private boolean search(LinkedList<Control> list, Interaction inter) 252 { 253 if (list.getLast().getControlled().contains(inter)) return true; 254 255 for (Process process : list.getLast().getControlled()) 256 { 257 if (process instanceof Control) 258 { 259 // prevent searching in cycles 260 if (list.contains(process)) continue; 261 262 list.add((Control) process); 263 if (search(list, inter)) return true; 264 else list.removeLast(); 265 } 266 } 267 return false; 268 } 269 270 /** 271 * Gets input ot output participants of the Conversion. 272 * @param conv Conversion to get participants 273 * @param type input or output 274 * @return related participants 275 */ 276 protected Set<PhysicalEntity> getConvParticipants(Conversion conv, RelType type) 277 { 278 ConversionDirectionType dir = getDirection(conv); 279 280 if (dir == ConversionDirectionType.REVERSIBLE) 281 { 282 HashSet<PhysicalEntity> set = new HashSet<PhysicalEntity>(conv.getLeft()); 283 set.addAll(conv.getRight()); 284 return set; 285 } 286 else if (dir == ConversionDirectionType.RIGHT_TO_LEFT) 287 { 288 return type == RelType.INPUT ? conv.getRight() : conv.getLeft(); 289 } 290 else return type == RelType.OUTPUT ? conv.getRight() : conv.getLeft(); 291 } 292 293 /** 294 * Searches pathways that contains this conversion for the possible directions. If both 295 * directions exist, then the result is reversible. 296 * @param conv the conversion 297 * @return direction inferred from pathway membership 298 */ 299 protected ConversionDirectionType findDirectionInPathways(Conversion conv) 300 { 301 Set<StepDirection> dirs = new HashSet<StepDirection>(); 302 for (PathwayStep step : conv.getStepProcessOf()) 303 { 304 if (step instanceof BiochemicalPathwayStep) 305 { 306 StepDirection dir = ((BiochemicalPathwayStep) step).getStepDirection(); 307 if (dir != null) dirs.add(dir); 308 } 309 } 310 if (dirs.size() > 1) return ConversionDirectionType.REVERSIBLE; 311 else if (!dirs.isEmpty()) 312 { 313 return dirs.iterator().next() == StepDirection.LEFT_TO_RIGHT ? 314 ConversionDirectionType.LEFT_TO_RIGHT : ConversionDirectionType.RIGHT_TO_LEFT; 315 } 316 else return null; 317 } 318 319 protected ConversionDirectionType convertStepDirection(StepDirection sdir) 320 { 321 if (sdir == StepDirection.LEFT_TO_RIGHT) return ConversionDirectionType.LEFT_TO_RIGHT; 322 if (sdir == StepDirection.RIGHT_TO_LEFT) return ConversionDirectionType.RIGHT_TO_LEFT; 323 return null; 324 } 325 326 /** 327 * Searches the controlling catalysis for possible direction of the conversion. 328 * @param conv the conversion 329 * @return direction inferred from catalysis objects 330 */ 331 protected ConversionDirectionType findDirectionInCatalysis(Conversion conv) 332 { 333 Set<ConversionDirectionType> dirs = new HashSet<ConversionDirectionType>(); 334 for (Control control : conv.getControlledOf()) 335 { 336 ConversionDirectionType dir = getCatalysisDirection(control); 337 if (dir != null) dirs.add(dir); 338 } 339 if (dirs.size() > 1) return ConversionDirectionType.REVERSIBLE; 340 else if (!dirs.isEmpty()) return dirs.iterator().next(); 341 else return null; 342 } 343 344 protected ConversionDirectionType getDirection(Conversion conv) 345 { 346 if (conv.getConversionDirection() != null) return conv.getConversionDirection(); 347 348 ConversionDirectionType catDir = findDirectionInCatalysis(conv); 349 ConversionDirectionType patDir = findDirectionInPathways(conv); 350 351 if (catDir != null && patDir != null && catDir != patDir) 352 return ConversionDirectionType.REVERSIBLE; 353 else if (catDir != null) return catDir; 354 else if (patDir != null) return patDir; 355 356 // No direction found! Assuming it is left-to-right. 357 else return ConversionDirectionType.LEFT_TO_RIGHT; 358 } 359}