/* * Copyright 2008 FatWire Corporation. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.fatwire.gst.foundation.facade.assetapi; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import com.fatwire.assetapi.common.AssetAccessException; import com.fatwire.assetapi.query.Condition; import com.fatwire.assetapi.query.ConditionFactory; import com.fatwire.assetapi.query.OpTypeEnum; import org.apache.commons.lang3.StringUtils; /** * Builds up a Condition from a string. * <p> * Sample query strings are: * <ul> * <li>name='foo'</li> * <li>name = 'foo'</li> * <li>name = foo</li> * <li>name= 'foo bar'</li> * <li>size=[1,2]</li> * <li>size{10,250}</li> * <li>name!='foo'</li> * </ul> * Whitespace is not significant outside single quotes. * * * @author Dolf Dijkstra * @since Mar 29, 2011 * @deprecated - com.fatwire.gst.foundation.facade and all subpackages have moved to the tools.gsf.facade package */ public class ConditionParser { enum Operator { EQUALS("="), NOT_EQUALS("!="), LESS_THAN("<"), LESS_THAN_EQUALS("<="), GREATER_THAN(">"), GREATER_THAN_EQUALS( ">="), BETWEEN("={"), BETWEEN_EXCLUDING("=!{"), LIKE("~"), RICHTEXT("#"); private final String op; Operator(final String op) { this.op = op; } }; private enum State { ATTRIBUTE, OP, VALUE }; interface ParserState { ParserState parse(char c); String toValue(); } static class AttributeState implements ParserState { private final StringBuilder value = new StringBuilder(); private boolean quoted = false; private final ParserState next; AttributeState(final ParserState next) { this.next = next; } public ParserState parse(final char c) { if (Character.isWhitespace(c)) { if (quoted) { value.append(c); } else { return next; } } else if (c == '"' || c == '\'') { if (quoted) { quoted = false; return next; } else { quoted = true; } } else if ("=!<>~{}".indexOf(c) != -1) { if (quoted) { value.append(c); } else if (value.length() > 0) { next.parse(c); return next; } } else { value.append(c); } return this; } public String toValue() { return value.toString(); } } static class OperatorState implements ParserState { private final StringBuilder value = new StringBuilder(); private final ParserState next; OperatorState(final ParserState next) { this.next = next; } public ParserState parse(final char c) { if ("=!<>~{".indexOf(c) != -1) { value.append(c); } else { next.parse(c); return next; } return this; } public String toValue() { return value.toString(); } } static class ValueState implements ParserState { private final StringBuilder value = new StringBuilder(); private boolean quoted = false; public ParserState parse(final char c) { if (Character.isWhitespace(c)) { if (quoted) { value.append(c); } else if (value.length() > 0) { value.append(c); } } else if (c == '"' || c == '\'') { quoted = !quoted; value.append(c); } else if ("{}".indexOf(c) != -1) { // brackets must always be quoted in values if (quoted) { value.append(c); } } else { value.append(c); } return this; } public String toValue() { return value.toString(); } } public Condition parse(final String s) { final ValueState valueState = new ValueState(); final OperatorState operatorState = new OperatorState(valueState); final AttributeState attributeState = new AttributeState(operatorState); ParserState state = attributeState; final char[] c = s.trim().toCharArray(); for (int i = 0; i < c.length; i++) { state = state.parse(c[i]); } final String attName = attributeState.toValue(); if (StringUtils.isBlank(attName)) { throw new IllegalArgumentException("No attribute name found in '" + s + "'."); } OpTypeEnum opType; try { opType = toOpType(operatorState.toValue()); } catch (final Exception e) { final IllegalArgumentException e2 = new IllegalArgumentException("No operator found in '" + s + "'. " + e.getMessage()); e2.initCause(e); throw e2; } final String value = valueState.toValue(); if (opType == OpTypeEnum.BETWEEN) { final String[] parts = valueSplit(value); if (parts.length != 2) { throw new IllegalArgumentException("Between condition does not two comma-seperated values in '" + s + "'. "); } try { return new ConditionFactory().createBetweenCondition(attName, parts[0], parts[1]); } catch (final AssetAccessException e) { final RuntimeAssetAccessException e1 = new RuntimeAssetAccessException(e.getMessage()); e1.initCause(e); throw e1; } } else if (opType == OpTypeEnum.EQUALS && value.startsWith("[") && value.endsWith("]")) { final String[] parts = valueSplit(value.substring(1, value.length() - 1)); if (parts.length < 1) { throw new IllegalArgumentException("Equals condition with multiple values does have any values: '" + s + "'. "); } return ConditionFactory.createCondition(attName, opType, Arrays.asList(parts)); } else if (opType == OpTypeEnum.NOT_EQUALS && value.startsWith("[") && value.endsWith("]")) { final String[] parts = valueSplit(value.substring(1, value.length() - 1)); if (parts.length < 1) { throw new IllegalArgumentException("Equals condition with multiple values does have any values: '" + s + "'. "); } Condition condition = null; for (final String part : parts) { final Condition cc = ConditionFactory.createCondition(attName, opType, part); if (condition == null) { condition = cc; } else { condition = condition.and(cc); } } return condition; } return ConditionFactory.createCondition(attName, opType, unquote(value)); } private String unquote(final String value) { if (StringUtils.isBlank(value)) { return value; } final char c = value.charAt(0); if (c == '\'' || c == '"') { if (value.length() < 3) { return ""; } return value.substring(1, value.length() - 1); } return value; } public String[] valueSplit(final String s) { final List<String> list = new LinkedList<String>(); boolean quoted = false; final char[] c = s.toCharArray(); final StringBuilder cur = new StringBuilder(); for (int i = 0; i < c.length; i++) { if (c[i] == ',') { if (quoted) { cur.append(c[i]); } else { list.add(cur.toString()); cur.setLength(0); } } else if (c[i] == '\'') { quoted = !quoted; } else { cur.append(c[i]); } } list.add(cur.toString()); return list.toArray(new String[0]); } OpTypeEnum toOpType(final StringBuilder op) { return toOpType(op.toString().toLowerCase()); } OpTypeEnum toOpType(final String op) { if (StringUtils.isBlank(op)) { throw new IllegalArgumentException("Operator can not be blank."); } if ("=".equals(op)) { return OpTypeEnum.EQUALS; } else if ("!=".equals(op)) { return OpTypeEnum.NOT_EQUALS; } else if ("<".equals(op)) { return OpTypeEnum.LESS_THAN; } else if (">".equals(op)) { return OpTypeEnum.GREATER_THAN; } else if ("{".equals(op)) { return OpTypeEnum.BETWEEN; } else if ("~".equals(op)) { return OpTypeEnum.LIKE; } else if ("#".equals(op)) { return OpTypeEnum.RICHTEXT; } throw new IllegalArgumentException("Can't decode operator in " + op); } }