001/* 002 * The contents of this file are subject to the license and copyright 003 * detailed in the LICENSE and NOTICE files at the root of the source 004 * tree. 005 */ 006package org.fcrepo.search.api; 007 008import java.util.regex.Pattern; 009 010/** 011 * A data structure representing a search condition. 012 * 013 * @author dbernstein 014 */ 015public class Condition { 016 public enum Operator { 017 LTE("<="), 018 GTE(">="), 019 EQ("="), 020 GT(">"), 021 LT("<"); 022 023 private String value; 024 025 Operator(final String value) { 026 this.value = value; 027 } 028 029 public String getStringValue() { 030 return this.value; 031 } 032 033 public static Operator fromString(final String str) { 034 for (final Operator o : Operator.values()) { 035 if (o.value.equals(str)) { 036 return o; 037 } 038 } 039 040 throw new IllegalArgumentException("Value " + str + " not recognized."); 041 } 042 043 } 044 045 public enum Field { 046 FEDORA_ID, 047 MODIFIED, 048 CREATED, 049 CONTENT_SIZE, 050 MIME_TYPE, 051 RDF_TYPE; 052 053 @Override 054 public String toString() { 055 return super.toString().toLowerCase(); 056 } 057 058 public static Field fromString(final String fieldStr) { 059 return Field.valueOf(fieldStr.toUpperCase()); 060 } 061 } 062 063 /* A regex for parsing the value of a "condition" query parameter which follows the format 064 * [field_name][operation][object] 065 * The field name is composed of at least one character and can contain alpha number characters and underscores. 066 * The operation can equal "=", "<", ">", "<=" or ">=" 067 * The object can be anything but cannot start with >, <, and =. 068 */ 069 final static Pattern CONDITION_REGEX = Pattern.compile("([a-zA-Z0-9_]+)([><=]|<=|>=)([^><=].*)"); 070 071 072 private Field field; 073 private Operator operator; 074 private String object; 075 076 /** 077 * Internal constructor 078 * 079 * @param field The search field (condition subject) 080 * @param operator The operator (condition predicate) 081 * @param object The object (condition object) 082 */ 083 private Condition(final Field field, final Operator operator, final String object) { 084 this.field = field; 085 this.operator = operator; 086 this.object = object; 087 } 088 089 /** 090 * Field accessor 091 * 092 * @return the field 093 */ 094 public Field getField() { 095 return field; 096 } 097 098 /** 099 * Operator accessor 100 * @return the operator 101 */ 102 public Operator getOperator() { 103 return operator; 104 } 105 106 /** 107 * @return the object portion of the condition 108 */ 109 public String getObject() { 110 return object; 111 } 112 113 @Override 114 public String toString() { 115 return this.field.toString().toLowerCase() + operator + object; 116 } 117 118 /** 119 * Parses a string expression into a Condition object. 120 * @param expression The condition as a string expression. 121 * @return The condition 122 * @throws InvalidConditionExpressionException if we can't parse the string into a Condition. 123 */ 124 public static Condition fromExpression(final String expression) throws InvalidConditionExpressionException { 125 final var m = CONDITION_REGEX.matcher(expression); 126 if (m.matches()) { 127 final var field = Field.fromString(m.group(1)); 128 final var operation = Operator.fromString(m.group(2)); 129 final var object = m.group(3); 130 return fromEnums(field, operation, object); 131 } 132 133 throw new InvalidConditionExpressionException(expression); 134 } 135 136 public static Condition fromEnums(final Field field, final Operator operator, final String expression) { 137 return new Condition(field, operator, expression); 138 } 139}