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