001package org.biopax.paxtools.pattern.constraint; 002 003import org.biopax.paxtools.model.BioPAXElement; 004import org.biopax.paxtools.pattern.MappedConst; 005import org.biopax.paxtools.pattern.Match; 006 007import java.util.Collection; 008import java.util.HashSet; 009import java.util.Set; 010 011/** 012 * Checks if generated elements has or has not a specific value for a field, or the field value of 013 * another set of generated elements. 014 * 015 * @author Ozgun Babur 016 */ 017public class FieldOfMultiple extends Field 018{ 019 /** 020 * Generative constraint for first group of generated elements, to use on first mapped element. 021 */ 022 protected MappedConst con1; 023 024 /** 025 * Generative constraint for second group of generated elements, to use on second mapped 026 * element. 027 */ 028 protected MappedConst con2; 029 030 /** 031 * Constructor with accessor string for the field value of the element and the desired value. If 032 * the desired value is EMPTY, then emptiness is checked. If it is USE_SECOND_ARG, then the 033 * second mapped element is used as the desired value. If a filed of the second element is 034 * desired then the other constructor should be used. 035 * 036 * @param con the generative constraint of size 2 whose generated values will be checked 037 * @param accessorString accessor string for the element 038 * @param oper operation 039 * @param value desired value 040 */ 041 public FieldOfMultiple(MappedConst con, String accessorString, Operation oper, Object value) 042 { 043 super(accessorString, oper, value); 044 con1 = con; 045 } 046 047 /** 048 * Constructor with accessor strings for the field value of the element and the desired value 049 * that will be reached from the second element. 050 * 051 * @param con1 the generative constraint of size 2 for the first group of elements 052 * @param accessorString1 accessor string for the first element 053 * @param con2 the generative constraint of size 2 for the second group of elements 054 * @param accessorString2 accessor string for the second element 055 * @param oper operation 056 */ 057 public FieldOfMultiple(MappedConst con1, String accessorString1, MappedConst con2, 058 String accessorString2, Operation oper) 059 { 060 super(accessorString1, accessorString2, oper); 061 this.con1 = con1; 062 this.con2 = con2; 063 } 064 065 /** 066 * Size of this constraint is one less than con1 if con2 is null, otherwise it is two less than 067 * the total of size of con1 and con2. 068 * @return the size based on con1 and con2 069 */ 070 @Override 071 public int getVariableSize() 072 { 073 return con1.getVariableSize() + (con2 != null ? con2.getVariableSize() - 2 : -1); 074 } 075 076 /** 077 * Checks if the generated elements from the first mapped element has either the desired value, 078 * or has some value in common with the elements generated from second mapped element. 079 * @param match current pattern match 080 * @param ind mapped indices 081 * @return true if a value match is found 082 */ 083 @Override 084 public boolean satisfies(Match match, int... ind) 085 { 086 assertIndLength(ind); 087 088 // Collect values of the element group 089 Set values = new HashSet(); 090 for (BioPAXElement gen : con1.generate(match, ind)) 091 { 092 values.addAll(pa1.getValueFromBean(gen)); 093 } 094 095 // If emptiness is desired, check that 096 if (value == EMPTY) return values.isEmpty(); 097 098 // If cannot be empty, check it 099 if (oper == Operation.NOT_EMPTY_AND_NOT_INTERSECT && values.isEmpty()) return false; 100 101 // If the second element is desired value, check that 102 else if (value == USE_SECOND_ARG) 103 { 104 BioPAXElement q = match.get(ind[1]); 105 return oper == Operation.INTERSECT ? values.contains(q) : !values.contains(q); 106 } 107 108 // If element group is compared to preset value, but the value is actually a collection, 109 // then iterate the collection, see if any of them matches 110 else if (value instanceof Collection) 111 { 112 Collection query = (Collection) value; 113 values.retainAll(query); 114 115 if (oper == Operation.INTERSECT) return !values.isEmpty(); 116 else return values.isEmpty(); 117 } 118 119 // If two set of elements should share a field value, check that 120 else if (pa2 != null) 121 { 122 // Collect values of the second group 123 Set others = new HashSet(); 124 for (BioPAXElement gen : con2.generate(match, ind)) 125 { 126 others.addAll(pa2.getValueFromBean(gen)); 127 } 128 129 switch (oper) 130 { 131 case INTERSECT: 132 others.retainAll(values); 133 return !others.isEmpty(); 134 case NOT_INTERSECT: 135 others.retainAll(values); 136 return others.isEmpty(); 137 case NOT_EMPTY_AND_NOT_INTERSECT: 138 if (others.isEmpty()) return false; 139 others.retainAll(values); 140 return others.isEmpty(); 141 default: throw new RuntimeException("Unhandled operation: " + oper); 142 } 143 } 144 145 // Check if the element field values contain the parameter value 146 else if (oper == Operation.INTERSECT) return values.contains(value); 147 else return !values.contains(value); 148 } 149}