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}