/* The contents of this file are subject to the license and copyright terms * detailed in the license directory at the root of the source tree (also * available online at http://fedora-commons.org/license/). */ package fedora.server.search; import java.util.ArrayList; import java.util.List; import fedora.server.errors.InvalidOperatorException; import fedora.server.errors.QueryParseException; /** * @author Chris Wilper */ public class Condition { private final String m_property; private final Operator m_operator; private final String m_value; public Condition(String property, Operator operator, String value) throws QueryParseException { m_property = property; m_operator = operator; if (value.indexOf("'") != -1) { throw new QueryParseException("Query cannot contain the ' character."); } m_value = value; } public Condition(String property, String operator, String value) throws InvalidOperatorException, QueryParseException { m_property = property; m_operator = Operator.fromAbbreviation(operator); if (value.indexOf("'") != -1) { throw new QueryParseException("Query cannot contain the ' character."); } m_value = value; } /** * Gets a List of Conditions from a string like: a=x b~'that\'s' c>='z' * * @param query * The query string. * @return The Conditions. */ public static List<Condition> getConditions(String query) throws QueryParseException { StringBuffer prop = new StringBuffer(); Operator oper = null; StringBuffer val = new StringBuffer(); ArrayList<Condition> ret = new ArrayList<Condition>(); boolean inProp = true; boolean inValue = false; boolean firstValueChar = false; boolean valueStartsWithQuote = false; for (int i = 0; i < query.length(); i++) { char c = query.charAt(i); if (inProp) { if (c == ' ') { throw new QueryParseException("Found <space> at character " + i + " but expected <operator> or <alphanum>"); } else if (c == '=') { oper = Operator.EQUALS; inProp = false; inValue = true; firstValueChar = true; } else if (c == '~') { oper = Operator.CONTAINS; inProp = false; inValue = true; firstValueChar = true; } else if (c == '>') { if (i + 1 < query.length()) { char d = query.charAt(i + 1); if (d == '=') { i++; oper = Operator.GREATER_OR_EQUAL; } else { oper = Operator.GREATER_THAN; } inProp = false; inValue = true; firstValueChar = true; } else { throw new QueryParseException("Found <end-of-string> " + "immediately following '>' operator, but " + "expected a value."); } } else if (c == '<') { if (i + 1 < query.length()) { char d = query.charAt(i + 1); if (d == '=') { i++; oper = Operator.LESS_OR_EQUAL; } else { oper = Operator.LESS_THAN; } inProp = false; inValue = true; firstValueChar = true; } else { throw new QueryParseException("Found <end-of-string> " + "immediately following '<' operator, but " + "expected a value."); } } else { prop.append(c); } } else if (inValue) { if (prop.toString().length() == 0) { throw new QueryParseException("Found " + "operator but expected a non-zero length " + "property."); } if (firstValueChar) { // allow ', and mark it if it's there, add one to i if (c == '\'') { i++; if (i >= query.length()) { throw new QueryParseException("Found <end-of-string> " + "immediately following start quote, but " + "expected a value."); } c = query.charAt(i); valueStartsWithQuote = true; } firstValueChar = false; } if (c == '\'') { if (!valueStartsWithQuote) { throw new QueryParseException("Found ' character in " + "value at position " + i + ", but the value " + "did not start with a string, so this can't " + " be a value terminator."); } // end of value part // next must be space or empty... check i++; if (i < query.length()) { if (query.charAt(i) != ' ') { throw new QueryParseException("Found value-terminator " + "' but it was not followed by <end-of-string> " + "or <space>."); } } ret .add(new Condition(prop.toString(), oper, val .toString())); prop = new StringBuffer(); oper = null; val = new StringBuffer(); inValue = false; inProp = true; valueStartsWithQuote = false; } else if (c == '\\') { i++; if (i >= query.length()) { throw new QueryParseException("Found character-escaping " + "character as last item in string."); } val.append(query.charAt(i)); } else if (c == ' ') { // end of value part... or inside string? if (valueStartsWithQuote) { // was inside string..ok val.append(c); } else { // end of value part...cuz not quotes ret.add(new Condition(prop.toString(), oper, val .toString())); prop = new StringBuffer(); oper = null; val = new StringBuffer(); inValue = false; inProp = true; } } else if (c == '=') { throw new QueryParseException("Found <operator> at position " + i + ", but expected <value>"); } else if (c == '~') { throw new QueryParseException("Found <operator> at position " + i + ", but expected <value>"); } else if (c == '>') { throw new QueryParseException("Found <operator> at position " + i + ", but expected <value>"); } else if (c == '<') { throw new QueryParseException("Found <operator> at position " + i + ", but expected <value>"); } else { val.append(c); } } } if (inProp) { if (prop.toString().length() > 0) { throw new QueryParseException("String ended before operator " + "was found"); } } if (inValue) { if (valueStartsWithQuote) { throw new QueryParseException("String ended before quoted value" + "'s ending quote."); } ret.add(new Condition(prop.toString(), oper, val.toString())); } return ret; } public String getProperty() { return m_property; } public Operator getOperator() { return m_operator; } public String getValue() { return m_value; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (o == null) { return false; } if (!o.getClass().equals(this.getClass())) { return false; } Condition that = (Condition) o; return equivalent(m_property, that.m_property) && equivalent(m_value, that.m_value) && equivalent(m_operator, that.m_operator); } @Override public int hashCode() { return m_property.hashCode() ^ m_operator.hashCode() ^ m_value.hashCode(); } @Override public String toString() { return "Condition[" + m_property + m_operator + m_value + "]"; } private boolean equivalent(Object one, Object two) { return one == null ? two == null : one.equals(two); } }