001/**
002 * Copyright 2014 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;
017
018import java.util.ArrayList;
019import java.util.Iterator;
020import java.util.List;
021
022import org.springframework.jdbc.core.JdbcTemplate;
023
024import core.tut.pori.dao.clause.SQLClause;
025import core.tut.pori.dao.clause.SQLClause.SQLType;
026import core.tut.pori.dao.clause.UpdateClause;
027import core.tut.pori.dao.clause.WhereClause;
028import core.tut.pori.datatypes.IntPrimitiveList;
029import core.tut.pori.http.parameters.Limits;
030
031/**
032 * Helper class, which can be used to build an SQL UPDATE clause.
033 *
034 */
035public class SQLUpdateBuilder {
036  private Limits _limits = null;
037  private String _tableName = null;
038  private ArrayList<UpdateClause> _updateClauses = new ArrayList<>();
039  private ArrayList<WhereClause> _whereClauses = null;
040  
041  /**
042   * 
043   * @param table
044   */
045  public SQLUpdateBuilder(String table){
046    _tableName = table;
047  }
048  
049  /**
050   * clear the currently set list of where clauses
051   */
052  public void clearWhereClauses(){
053    _whereClauses = null;
054  }
055  
056  /**
057   * 
058   * @return number of added where clauses
059   */
060  public int getWhereClauseCount() {
061    return (_whereClauses == null ? 0 : _whereClauses.size());
062  }
063  
064  /**
065   * @param startItem the startItem to set, negative value disables limit
066   */
067  public void setStartItem(int startItem) {
068    if(_limits == null){
069      _limits = new Limits(startItem, Limits.DEFAULT_MAX_ITEMS);
070    }else{
071      _limits.setTypeLimits(startItem, _limits.getEndItem(), null);
072    }
073  }
074
075  /**
076   * @param maxItems the maxItems to set, negative value disables limit
077   */
078  public void setMaxItems(int maxItems) {
079    if(_limits == null){
080      _limits = new Limits(0, maxItems-1);
081    }else{
082      _limits.setTypeLimits(_limits.getStartItem(), maxItems-1, null);
083    }
084  }
085  
086  /**
087   * 
088   * @param limits if null, this is a no-op
089   */
090  public void setLimits(Limits limits){
091    _limits = limits;
092  }
093  
094  /**
095   * 
096   * @return number of added update clauses
097   */
098  public int getUpdateClauseCount() {
099    return _updateClauses.size();
100  }
101  
102  /**
103   * null clause will be silently ignored
104   * 
105   * @param clause
106   */
107  public void addUpdateClause(UpdateClause clause){
108    if(clause != null){
109      _updateClauses.add(clause);
110    }
111  }
112  
113  /**
114   * clear the list of update clauses if any
115   */
116  public void clearUpdateClauses(){
117    _updateClauses.clear();
118  }
119  
120  /**
121   * 
122   * @param clause
123   */
124  public void addWhereClause(WhereClause clause){
125    if(_whereClauses == null){
126      _whereClauses = new ArrayList<>();
127    }
128    _whereClauses.add(clause);
129  }
130
131  /**
132   * execute the statement on the given template
133   * @param template
134   * @return number of rows affected
135   */
136  public int execute(JdbcTemplate template){
137    return template.update(toSQLString(), getValues(), getValueTypes());
138  }
139  
140  /**
141   * execute the statement on the given template
142   * @param template
143   * @param type
144   * @return number of rows affected
145   */
146  public int execute(JdbcTemplate template, String type){
147    return template.update(toSQLString(type), getValues(), getValueTypes());
148  }
149  
150  /**
151   * 
152   * @return array of parameter values
153   */
154  public Object[] getValues(){
155    List<Object> objects = new ArrayList<>();
156    for(Iterator<? extends SQLClause> iter = _updateClauses.iterator();iter.hasNext();){
157      SQLClause c = iter.next();
158      Object[] o = c.getValues();
159      if(o != null){
160        for(int i=0;i<o.length;++i){
161          objects.add(o[i]);
162        }
163      }
164    }
165    
166    if(_whereClauses != null){
167      for(Iterator<? extends SQLClause> iter = _whereClauses.iterator();iter.hasNext();){
168        SQLClause c = iter.next();
169        Object[] o = c.getValues();
170        if(o != null){
171          for(int i=0;i<o.length;++i){
172            objects.add(o[i]);
173          }
174        }
175      }
176    }
177    
178    if(objects.isEmpty()){
179      return null;
180    }else{
181      return objects.toArray();
182    }
183  }
184  
185  /**
186   * 
187   * @return array of parameter types
188   */
189  public int[] getValueTypes(){
190    IntPrimitiveList list = new IntPrimitiveList();
191    for(Iterator<? extends SQLClause> iter = _updateClauses.iterator();iter.hasNext();){
192      SQLClause c = iter.next();
193      SQLType[] t = c.getValueTypes();
194      if(t != null){
195        for(int i=0;i<t.length;++i){
196          list.add(t[i].toInt());
197        }
198      }
199    }
200    
201    if(_whereClauses != null){
202      for(Iterator<? extends SQLClause> iter = _whereClauses.iterator();iter.hasNext();){
203        SQLClause c = iter.next();
204        SQLType[] t = c.getValueTypes();
205        if(t != null){
206          for(int i=0;i<t.length;++i){
207            list.add(t[i].toInt());
208          }
209        }
210      }
211    }
212    
213    return list.toArray();
214  }
215  
216  /**
217   * 
218   * @return the query as a SQL string
219   */
220  public String toSQLString(){
221    return toSQLString(null);
222  }
223
224  /**
225   * 
226   * @param type
227   * @return the query as an SQL string using the given type of typed parameters (e.g. Limits). See {@link core.tut.pori.http.parameters.Limits}
228   */
229  public String toSQLString(String type){
230    StringBuilder sql = new StringBuilder("UPDATE ");
231    sql.append(_tableName);
232    sql.append(" SET ");
233    
234    /* update clauses */
235    Iterator<UpdateClause> iter = _updateClauses.iterator();
236    iter.next().toSQLString(sql); // let it throw out-of-bounds if there are no update clauses
237    while(iter.hasNext()){
238      sql.append(',');
239      iter.next().toSQLString(sql);
240    }
241
242    /* create where clauses */
243    if(_whereClauses != null){
244      sql.append(" WHERE ");
245      Iterator<WhereClause> witer = _whereClauses.iterator();
246      witer.next().toSQLString(sql);
247      while(witer.hasNext()){
248        WhereClause clause = witer.next();
249        sql.append(clause.getClauseType().toClauseString());
250        clause.toSQLString(sql);
251      }
252    }
253    
254    /* create limit */
255    if(_limits != null){
256      sql.append(" LIMIT ");
257      sql.append(_limits.getStartItem(type));
258      sql.append(',');
259      sql.append(_limits.getMaxItems());
260    }
261    
262    return sql.toString();
263  }
264}