/** * Copyright (C) 2012 the original author or authors. * * 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 co.jirm.orm.builder; import static com.google.common.base.Strings.nullToEmpty; import static co.jirm.core.util.JirmPrecondition.check; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; public abstract class ImmutableCondition extends ImmutableParameterized<ImmutableCondition> implements Condition<ImmutableCondition> { protected final ConditionType conditionType; public abstract void accept(ConditionVisitor v); public static ImmutableCondition where(String custom) { if (nullToEmpty(custom).trim().isEmpty()) return where(); return new CustomCondition(custom); } public static ImmutableCondition where() { return new NoOp(); } ImmutableCondition(ImmutableList<Object> parameters, ImmutableMap<String, Object> nameParameters, ConditionType conditionType) { super(parameters, nameParameters); this.conditionType = conditionType; } ImmutableCondition(ImmutableCondition child, Object value) { super(child, value); this.conditionType = child.conditionType; } ImmutableCondition(ImmutableCondition child, String key, Object value) { super(child, key, value); this.conditionType = child.conditionType; } ImmutableCondition(ConditionType type) { conditionType = type; } public ImmutableCondition and(ImmutableCondition other) { if (other.conditionType == ConditionType.NOOP) return this; if (isNoOp()) return other; return CombinedCondition.newInstance(this, CombineType.AND, other); } public ImmutableCondition and(String sql) { if (isNoOp()) return where(sql); return this.and(new CustomCondition(sql)); } public ImmutableCondition or(ImmutableCondition other) { if (other.conditionType == ConditionType.NOOP) return this; if (isNoOp()) return other; return this.or(other); } public ImmutableCondition or(String sql) { if (isNoOp()) return where(sql); return CombinedCondition.newInstance(this, CombineType.OR, new CustomCondition(sql)); } public ImmutableCondition not() { if (isNoOp()) return this; return new NotCondition(this); } public boolean isNoOp() { return (this.conditionType == ConditionType.NOOP); } private final static class CombinedCondition extends ImmutableCondition { private final ImmutableList<ImmutableCondition> conditions; public static CombinedCondition newInstance(ImmutableCondition lh, CombineType operator, ImmutableCondition rh) { ConditionType conditionType = operator == CombineType.AND ? ConditionType.AND : ConditionType.OR; final ImmutableList<ImmutableCondition> conditions; final ImmutableList<Object> parameters; final ImmutableMap<String, Object> nameParameters; if (conditionType == lh.conditionType && conditionType == rh.conditionType) { conditions = ImmutableList.<ImmutableCondition>builder() .addAll(((CombinedCondition)lh).conditions) .addAll(((CombinedCondition)rh).conditions).build(); parameters = ImmutableList.<Object>builder().addAll(lh.getParameters()).addAll(rh.getParameters()).build(); nameParameters = ImmutableMap.<String, Object>builder() .putAll(lh.getNameParameters()).putAll(rh.getNameParameters()).build(); } else if (conditionType == lh.conditionType && ! rh.conditionType.isCombine()) { conditions = ImmutableList.<ImmutableCondition>builder() .addAll(((CombinedCondition)lh).conditions).add(rh).build(); parameters = ImmutableList.copyOf(lh.getParameters()); nameParameters = ImmutableMap.copyOf(lh.getNameParameters()); } else if (conditionType == rh.conditionType && ! lh.conditionType.isCombine()) { conditions = ImmutableList.<ImmutableCondition>builder() .addAll(((CombinedCondition)rh).conditions).add(lh).build(); parameters = ImmutableList.copyOf(rh.getParameters()); nameParameters = ImmutableMap.copyOf(rh.getNameParameters()); } else { conditions = ImmutableList.<ImmutableCondition>builder().add(lh).add(rh).build(); parameters = ImmutableList.of(); nameParameters = ImmutableMap.of(); } return new CombinedCondition(parameters, nameParameters, conditionType, conditions); } private CombinedCondition(ImmutableList<Object> parameters, ImmutableMap<String, Object> nameParameters, ConditionType conditionType, ImmutableList<ImmutableCondition> conditions) { super(parameters, nameParameters, conditionType); this.conditions = conditions; } @Override public void accept(ConditionVisitor v) { if (this.conditionType == ConditionType.AND) v.visitAnd(conditions, this); else v.visitOr(conditions, this); } private CombinedCondition(CombinedCondition child, Object value) { super(child, value); this.conditions = child.conditions; } private CombinedCondition(CombinedCondition child, String key, Object value) { super(child, key, value); this.conditions = child.conditions; } @Override public ImmutableCondition bind(String key, Object value) { return new CombinedCondition(this, key, value); } @Override public ImmutableCondition with(Object ... value ) { return new CombinedCondition(this, value[0]); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((conditions == null) ? 0 : conditions.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; CombinedCondition other = (CombinedCondition) obj; if (conditions == null) { if (other.conditions != null) return false; } else if (!conditions.equals(other.conditions)) return false; return true; } } private final static class CustomCondition extends ImmutableCondition { private final String sql; @Override public void accept(ConditionVisitor v) { v.visitSql(sql, this); } private CustomCondition(String sql) { super(ConditionType.CUSTOM); this.sql = sql; } private CustomCondition(CustomCondition child, Object value) { super(child, value); this.sql = child.sql; } private CustomCondition(CustomCondition child, String key, Object value) { super(child, key, value); this.sql = child.sql; } @Override public ImmutableCondition bind(String key, Object value) { return new CustomCondition(this, key, value); } @Override public ImmutableCondition with(Object ... value) { //TODO check args return new CustomCondition(this, value[0]); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((sql == null) ? 0 : sql.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; CustomCondition other = (CustomCondition) obj; if (sql == null) { if (other.sql != null) return false; } else if (!sql.equals(other.sql)) return false; return true; } } private final static class NotCondition extends ImmutableCondition { private final ImmutableCondition condition; @Override public void accept(ConditionVisitor visitor) { visitor.visitNot(condition); } public NotCondition(ImmutableCondition condition) { super(ConditionType.NOT); this.condition = condition; } private NotCondition(NotCondition child, Object value) { super(child, value); this.condition = child.condition; } private NotCondition(NotCondition child, String key, Object value) { super(child, key, value); this.condition = child.condition; } @Override public NotCondition bind(String key, Object value) { return new NotCondition(this, key, value); } @Override public ImmutableCondition with(Object ... value) { return new NotCondition(this, value[0]); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((condition == null) ? 0 : condition.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; NotCondition other = (NotCondition) obj; if (condition == null) { if (other.condition != null) return false; } else if (!condition.equals(other.condition)) return false; return true; } } private final static class NoOp extends ImmutableCondition { private NoOp() { super(ConditionType.NOOP); } @Override public void accept(ConditionVisitor v) { v.visitNoOp(); } private NoOp(ImmutableCondition child, Object value) { super(child, value); throw check.stateInvalid("Cannot set parameters on this condition."); } private NoOp(ImmutableCondition child, String key, Object value) { super(child, key, value); throw check.stateInvalid("Cannot set parameters on this condition."); } @Override public ImmutableCondition bind(String key, Object value) { throw check.stateInvalid("Cannot set parameters on this condition."); } @Override public ImmutableCondition with(Object ... value) { throw check.stateInvalid("Cannot set parameters on this condition."); } } public enum ConditionType { CUSTOM, NOOP, AND, OR, NOT; public boolean isCombine() { return this == AND || this == OR; } } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((conditionType == null) ? 0 : conditionType.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; ImmutableCondition other = (ImmutableCondition) obj; if (conditionType != other.conditionType) return false; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); ConditionVisitors.conditionVisitor(sb).startOn(this); return sb.toString(); } }