/** * Matches each JexlVCMatchExp exp against the data contained in vc, and returns a map from these * expressions to true (if they matched) or false (if they didn't). This the best way to apply JEXL * expressions to VariantContext records. Use initializeMatchExps() to create the list of JexlVCMatchExp * expressions. * * @param vc variant context * @param exps expressions * @return true if there is a match */ public static Map<JexlVCMatchExp, Boolean> match(VariantContext vc, Collection<JexlVCMatchExp> exps) { return new JEXLMap(exps,vc); }
/** * Construct a new JEXLMap which can evaluate expressions against a specific genotype and variant context * @param jexlCollection collection of expressions to be evaluated * @param vc VariantContext to evaluate expressions against * @param g genotype to evaluate expressions against, may be null * @param howToTreatMissingValues how missing values in vc and g should be treated */ public JEXLMap(final Collection<JexlVCMatchExp> jexlCollection, final VariantContext vc, final Genotype g, final JexlMissingValueTreatment howToTreatMissingValues) { this.jexl = initializeMap(jexlCollection); this.vc = vc; this.g = g; this.howToTreatMissingValues = howToTreatMissingValues; }
public JEXLMap(Collection<JexlVCMatchExp> jexlCollection, VariantContext vc, Genotype g) { this.vc = vc; this.g = g; initialize(jexlCollection); }
public Boolean get(Object o) { // if we've already determined the value, return it if (jexl.containsKey(o) && jexl.get(o) != null) return jexl.get(o); // try and cast the expression JexlVCMatchExp e = (JexlVCMatchExp) o; evaluateExpression(e); return jexl.get(e); }
@Test(dataProvider = "getMissingValueTestData") public void testMissingBehavior(VariantContext vc, JexlMissingValueTreatment missingValueTreatment, boolean expected, Class<? extends Exception> expectedException){ final JEXLMap jexlMap = new JEXLMap(Collections.singletonList(missingValueExpression), vc, null, missingValueTreatment); if(expectedException == null) { Assert.assertEquals((boolean) jexlMap.get(missingValueExpression), expected); } else { Assert.assertThrows(expectedException, () -> jexlMap.get(missingValueExpression)); } }
/** * evaulate a JexlVCMatchExp's expression, given the current context (and setup the context if it's null) * @param exp the JexlVCMatchExp to evaluate */ private void evaluateExpression(JexlVCMatchExp exp) { // if the context is null, we need to create it to evaluate the JEXL expression if (this.jContext == null) createContext(); try { final Boolean value = (Boolean) exp.exp.evaluate(jContext); // treat errors as no match jexl.put(exp, value == null ? false : value); } catch (Exception e) { // if exception happens because variable is undefined (i.e. field in expression is not present), evaluate to FALSE // todo - might be safer if we explicitly checked for an exception type, but Apache's API doesn't seem to have that ability if (e.getMessage().contains("undefined variable")) jexl.put(exp,false); else throw new IllegalArgumentException(String.format("Invalid JEXL expression detected for %s with message %s", exp.name, e.getMessage())); } }
addAttributesToMap(infoMap, vc.getAttributes());
/** * get all the values of the map. This is an expensive call, since it evaluates all keys that haven't * been evaluated yet. This is fine if you truely want all the keys, but if you only want a portion, or know * the keys you want, you would be better off using get() to get them by name. * @return a collection of boolean values, representing the results of all the variants evaluated */ public Collection<Boolean> values() { // this is an expensive call for (JexlVCMatchExp exp : jexl.keySet()) if (jexl.get(exp) == null) evaluateExpression(exp); return jexl.values(); }
/** * Evaluates a {@link JexlVCMatchExp}'s expression, given the current context (and setup the context if it's {@code null}). * * @param exp the {@link JexlVCMatchExp} to evaluate * @return true if the expression matched the context * @throws IllegalArgumentException when {@code exp} is {@code null}, or * when the Jexl expression in {@code exp} fails to evaluate the JexlContext * constructed with the input VC or genotype. */ private boolean evaluateExpression(final JexlVCMatchExp exp) { // if the context is null, we need to create it to evaluate the JEXL expression if (this.jContext == null) { jContext = createContext(); } try { //TODO figure out of this can ever evaluate to null or if that isn't actually possible final Boolean value = (Boolean) exp.exp.evaluate(jContext); return value == null ? howToTreatMissingValues.getMissingValueOrExplode() : value; } catch (final JexlException.Variable e) { //this occurs when the jexl expression contained a literal that didn't match anything in the given context return howToTreatMissingValues.getMissingValueOrExplode(); } catch (final JexlException e) { // todo - might be better if no exception is caught here but let's user decide how to deal with them; note this will propagate to get() and values() throw new IllegalArgumentException(String.format("Invalid JEXL expression detected for %s", exp.name), e); } }
/** * Matches each JexlVCMatchExp exp against the data contained in vc/g, and returns a map from these * expressions to true (if they matched) or false (if they didn't). This the best way to apply JEXL * expressions to VariantContext records/genotypes. Use initializeMatchExps() to create the list of JexlVCMatchExp * expressions. * * @param vc variant context * @param g genotype * @param exps expressions * @return true if there is a match */ public static Map<JexlVCMatchExp, Boolean> match(VariantContext vc, Genotype g, Collection<JexlVCMatchExp> exps) { return new JEXLMap(exps,vc,g); }
/** * Get all the values of the map, i.e. the {@link Boolean} values. * This is an expensive call, since it evaluates all keys that haven't been evaluated yet. * This is fine if you truly want all the keys, but if you only want a portion, or know * the keys you want, you would be better off using get() to get them by name. * * Note: due to laziness, this accessor actually modifies the instance by possibly forcing evaluation of an Jexl expression. * * @return a collection of boolean values, representing the results of all the variants evaluated * * @throws IllegalArgumentException when any of the JexlVCMatchExp (i.e. keys) contains invalid Jexl expressions. */ @Override public Collection<Boolean> values() { for (final JexlVCMatchExp exp : jexl.keySet()) { jexl.computeIfAbsent(exp, k -> evaluateExpression(exp)); } return jexl.values(); }
/** * Evaluates a {@link JexlVCMatchExp}'s expression, given the current context (and setup the context if it's {@code null}). * * @param exp the {@link JexlVCMatchExp} to evaluate * @return true if the expression matched the context * @throws IllegalArgumentException when {@code exp} is {@code null}, or * when the Jexl expression in {@code exp} fails to evaluate the JexlContext * constructed with the input VC or genotype. */ private boolean evaluateExpression(final JexlVCMatchExp exp) { // if the context is null, we need to create it to evaluate the JEXL expression if (this.jContext == null) { jContext = createContext(); } try { //TODO figure out of this can ever evaluate to null or if that isn't actually possible final Boolean value = (Boolean) exp.exp.evaluate(jContext); return value == null ? howToTreatMissingValues.getMissingValueOrExplode() : value; } catch (final JexlException.Variable e) { //this occurs when the jexl expression contained a literal that didn't match anything in the given context return howToTreatMissingValues.getMissingValueOrExplode(); } catch (final JexlException e) { // todo - might be better if no exception is caught here but let's user decide how to deal with them; note this will propagate to get() and values() throw new IllegalArgumentException(String.format("Invalid JEXL expression detected for %s", exp.name), e); } }
/** * Construct a new JEXLMap which can evaluate expressions against a specific genotype and variant context * @param jexlCollection collection of expressions to be evaluated * @param vc VariantContext to evaluate expressions against * @param g genotype to evaluate expressions against, may be null * @param howToTreatMissingValues how missing values in vc and g should be treated */ public JEXLMap(final Collection<JexlVCMatchExp> jexlCollection, final VariantContext vc, final Genotype g, final JexlMissingValueTreatment howToTreatMissingValues) { this.jexl = initializeMap(jexlCollection); this.vc = vc; this.g = g; this.howToTreatMissingValues = howToTreatMissingValues; }
/** * Matches each {@link JexlVCMatchExp} exp against the data contained in {@code vc}, * and returns a map from these expressions to {@code true} (if they matched) or {@code false} (if they didn't). * This the best way to apply JEXL expressions to {@link VariantContext} records. * Use the various {@code initializeMatchExps()}'s to create the list of {@link JexlVCMatchExp} expressions. * * Expressions that contain literals not available in the VariantContext or Genotype will be treated as not matching * @param vc variant context * @param exps expressions * @return true if there is a match */ public static Map<JexlVCMatchExp, Boolean> match(VariantContext vc, Collection<JexlVCMatchExp> exps) { return new JEXLMap(exps,vc); }
/** * Get all the values of the map, i.e. the {@link Boolean} values. * This is an expensive call, since it evaluates all keys that haven't been evaluated yet. * This is fine if you truly want all the keys, but if you only want a portion, or know * the keys you want, you would be better off using get() to get them by name. * * Note: due to laziness, this accessor actually modifies the instance by possibly forcing evaluation of an Jexl expression. * * @return a collection of boolean values, representing the results of all the variants evaluated * * @throws IllegalArgumentException when any of the JexlVCMatchExp (i.e. keys) contains invalid Jexl expressions. */ @Override public Collection<Boolean> values() { for (final JexlVCMatchExp exp : jexl.keySet()) { jexl.computeIfAbsent(exp, k -> evaluateExpression(exp)); } return jexl.values(); }
/** * Matches each {@link JexlVCMatchExp} exp against the data contained in {@code vc}, * and returns a map from these expressions to {@code true} (if they matched) or {@code false} (if they didn't). * This the best way to apply JEXL expressions to {@link VariantContext} records. * Use the various {@code initializeMatchExps()}'s to create the list of {@link JexlVCMatchExp} expressions. * * Expressions that contain literals not available in the VariantContext or Genotype will be treated as not matching * @param vc variant context * @param exps expressions * @return true if there is a match */ public static Map<JexlVCMatchExp, Boolean> match(VariantContext vc, Collection<JexlVCMatchExp> exps) { return new JEXLMap(exps,vc); }
/** * Note: due to laziness, this accessor actually modifies the instance by possibly forcing evaluation of an Jexl expression. * * @throws IllegalArgumentException when {@code key} is {@code null} or * when any of the JexlVCMatchExp (i.e. keys) contains invalid Jexl expressions. */ @Override public Boolean get(Object key) { if (key == null) { throw new IllegalArgumentException("Query key is null"); } // if we've already determined the value, return it final Boolean value = jexl.get(key); if (jexl.containsKey(key) && value != null) { return value; } // otherwise cast the expression and try again final JexlVCMatchExp exp = (JexlVCMatchExp) key; final boolean matches = evaluateExpression(exp); jexl.put(exp, matches); return matches; }
/** * Matches each {@link JexlVCMatchExp} exp against the data contained in {@code vc}, {@code g}, * and returns a map from these expressions to {@code true} (if they matched) or {@code false} (if they didn't). * This the best way to apply JEXL expressions to {@link VariantContext} records. * Use the various {@code initializeMatchExps()}'s to create the list of {@link JexlVCMatchExp} expressions. * * @param vc variant context * @param g genotype * @param exps expressions * @param howToTreatMissingValues what to do if the jexl expression contains literals that aren't in the context * @return true if there is a match */ public static Map<JexlVCMatchExp, Boolean> match(VariantContext vc, Genotype g, Collection<JexlVCMatchExp> exps, JexlMissingValueTreatment howToTreatMissingValues) { return new JEXLMap(exps, vc, g, howToTreatMissingValues); }
/** * Note: due to laziness, this accessor actually modifies the instance by possibly forcing evaluation of an Jexl expression. * * @throws IllegalArgumentException when {@code key} is {@code null} or * when any of the JexlVCMatchExp (i.e. keys) contains invalid Jexl expressions. */ @Override public Boolean get(Object key) { if (key == null) { throw new IllegalArgumentException("Query key is null"); } // if we've already determined the value, return it final Boolean value = jexl.get(key); if (jexl.containsKey(key) && value != null) { return value; } // otherwise cast the expression and try again final JexlVCMatchExp exp = (JexlVCMatchExp) key; final boolean matches = evaluateExpression(exp); jexl.put(exp, matches); return matches; }
/** * Matches each {@link JexlVCMatchExp} exp against the data contained in {@code vc}, {@code g}, * and returns a map from these expressions to {@code true} (if they matched) or {@code false} (if they didn't). * This the best way to apply JEXL expressions to {@link VariantContext} records. * Use the various {@code initializeMatchExps()}'s to create the list of {@link JexlVCMatchExp} expressions. * * @param vc variant context * @param g genotype * @param exps expressions * @param howToTreatMissingValues what to do if the jexl expression contains literals that aren't in the context * @return true if there is a match */ public static Map<JexlVCMatchExp, Boolean> match(VariantContext vc, Genotype g, Collection<JexlVCMatchExp> exps, JexlMissingValueTreatment howToTreatMissingValues) { return new JEXLMap(exps, vc, g, howToTreatMissingValues); }