001/**
002 * Copyright 2015 Tampere University of Technology, Pori Department
003 * 
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * 
008 *   http://www.apache.org/licenses/LICENSE-2.0
009 * 
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package core.tut.pori.dao.clause;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.List;
021
022import org.apache.commons.lang3.ArrayUtils;
023
024/**
025 * A simple SQL WHERE AND clause.
026 * 
027 * This class will always prefix the clause with AND, and giving multiple values equals to SQL AND IN (?,?,...)
028 * 
029 * Passing null object, null array or empty array equals to providing the clause with SQL NULL. An array can also have a null value.
030 */
031public class AndClause extends WhereClause{
032  private String _column = null;
033  private SQLType[] _types = null;
034  private Object[] _values = null;
035  private boolean _not = false;
036  private boolean _hasNull = false;
037
038  @Override
039  public SQLType[] getValueTypes() {
040    return _types;
041  }
042
043  @Override
044  public Object[] getValues() {
045    return _values;
046  }
047  
048  /**
049   * 
050   * @param column
051   * @param values
052   */
053  public AndClause(String column, int[] values){
054    _column = column;
055    setValues(ArrayUtils.toObject(values), SQLType.INTEGER);
056  }
057  
058  /**
059   * 
060   * @param column
061   * @param values
062   */
063  public AndClause(String column, long[] values){
064    _column = column;
065    setValues(ArrayUtils.toObject(values), SQLType.LONG);
066  }
067  
068  /**
069   * 
070   * @param column
071   * @param values
072   */
073  public AndClause(String column, double[] values){
074    _column = column;
075    setValues(ArrayUtils.toObject(values), SQLType.DOUBLE);
076  }
077  
078  /**
079   * 
080   * @param column
081   * @param values
082   */
083  public AndClause(String column, boolean[] values){
084    _column = column;
085    setValues(ArrayUtils.toObject(values), SQLType.BOOLEAN);
086  }
087
088  /**
089   * @param column
090   * @param value can be null
091   * @param type type of data
092   */
093  public AndClause(String column, Object value, SQLType type){
094    setValues(new Object[]{value}, type);
095    _column = column;
096  }
097  
098  /**
099   * 
100   * @param column
101   * @param values
102   * @param type
103   */
104  public AndClause(String column, Collection<? extends Object> values, SQLType type){
105    _column = column;
106    Object[] o = null;
107    if(values != null){
108      o = values.toArray();
109    }
110    setValues(o, type);
111  }
112  
113  /**
114   * Note: this does creates an IN () clause based on the list of values, NOT multiple AND clauses
115   * that include all of the given values. This is because no column can have more than a single value,
116   * and thus, creating AND for all of the given values would not make sense.
117   * 
118   * @param column
119   * @param values can be null
120   * @param type type of data
121   */
122  public AndClause(String column, Object[] values, SQLType type){
123    setValues(values, type);
124    _column = column;
125  }
126  
127  /**
128   * 
129   * @param values
130   * @param type
131   */
132  public void setValues(Object[] values, SQLType type){
133    _values = null;
134    _types = null;
135    if(!ArrayUtils.isEmpty(values)){
136      List<SQLType> types = new ArrayList<>(values.length);
137      for(int i=0;i<values.length;++i){
138        if(values[i] == null){
139          _hasNull = true;
140          values = ArrayUtils.remove(values, i--); // remove the current value and decrease counter
141          continue;
142        }
143        types.add(type);
144      } // for
145      if(!types.isEmpty()){
146        _values = values;
147        _types = types.toArray(new SQLType[types.size()]);
148      }
149    }else{
150      _hasNull = true;
151    }
152  }
153
154  @Override
155  public void toSQLString(StringBuilder sql) {
156    sql.append('(');
157    if(_values != null){
158      sql.append(_column);
159      if(_not){
160        sql.append(" NOT IN (");
161      }else{
162        sql.append(" IN (");
163      }
164      sql.append('?');
165      for(int i=1;i<_values.length;++i){
166        sql.append(",?");
167      }
168      if(_hasNull){
169        sql.append(") OR ");
170      }else{
171        sql.append(')');
172      }
173    }
174    if(_hasNull){
175      sql.append(_column);
176      if(_not){
177        sql.append(" IS NOT NULL");
178      }else{
179        sql.append(" IS NULL");
180      }
181    }
182    sql.append(')');
183  }
184
185  /**
186   * 
187   * @param not set this to be NOT clause on true, remove NOT declaration on false
188   * @return this
189   */
190  public AndClause setNot(boolean not){
191    _not = not;
192    return this;
193  }
194
195  @Override
196  public ClauseType getClauseType() {
197    return ClauseType.AND;
198  }
199}