1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 package stats;
28 
29 import java.sql.Connection;
30 import java.sql.DriverManager;
31 import java.sql.ResultSet;
32 import java.sql.ResultSetMetaData;
33 import java.sql.Statement;
34 import java.util.Enumeration;
35 import java.util.Hashtable;
36 import java.util.StringTokenizer;
37 import java.util.Vector;
38 
39 /**
40  *
41  */
42 public class SQLExecution {
43 
44     protected Connection mConnection = null;
45     protected Statement mStatement = null;
46     protected String mJdbcClass = null;
47     protected String mDbURL = null;
48     protected String mUser = null;
49     protected String mPassword = null;
50     protected boolean m_bConnectionOpen = false;
51     protected boolean m_bDebug = false;
52 
53 
54     /** Creates a new instance of SQLExecution
55      * @param jdbcClass The jdbc class for the connection.
56      * @param dbUrl The url of the database.
57      * @param user The user for connecting the database.
58      * @param password The password of throws user.
59      */
60     public SQLExecution(String jdbcClass, String dbUrl, String user, String password) {
61         mJdbcClass = jdbcClass;
62         mUser = user;
63         mPassword = password;
64         mDbURL = dbUrl;
65     }
66 
67     /** Creates a new instance of SQLExecution with additional debug output.
68      * @param jdbcClass The jdbc class for the connection.
69      * @param dbUrl The url of the database.
70      * @param user The user for connecting the database.
71      * @param password The password of throws user.
72      * @param debug Write debug information, if true.
73      */
74     public SQLExecution(String jdbcClass, String dbUrl, String user, String password, boolean debug) {
75         mJdbcClass = jdbcClass;
76         mUser = user;
77         mPassword = password;
78         mDbURL = dbUrl;
79         m_bDebug = debug;
80     }
81 
82     /**
83      * Open a connection to the DataBase
84      * @return True, if no error occured.
85      */
86     public boolean openConnection() {
87         if(m_bConnectionOpen) return true;
88         try {
89             Class.forName(mJdbcClass);
90         } catch (ClassNotFoundException e) {
91             System.err.println("Couldn't find jdbc driver : " + e.getMessage());
92             return false;
93         }
94 
95         try {
96             // establish database connection
97             mConnection = DriverManager.getConnection(
98                                                 mDbURL, mUser, mPassword);
99             mStatement = mConnection.createStatement();
100         }
101         catch(java.sql.SQLException e) {
102             System.err.println("Couldn't establish a connection: " + e.getMessage());
103             return false;
104         }
105         m_bConnectionOpen = true;
106         return true;
107     }
108 
109     /**
110      * Close the connection to the DataBase
111      * @return True, if no error occured.
112      */
113     public boolean closeConnection() {
114         if (!m_bConnectionOpen) return true;
115         try {
116             // close database connection
117             mStatement.close();
118             mConnection.close();
119         }
120         catch(java.sql.SQLException e) {
121             System.err.println("Couldn't close the connection: " + e.getMessage());
122             return false;
123         }
124         m_bConnectionOpen = false;
125         return true;
126     }
127 
128     /**
129      * Execute an sql command.
130      * @param command The command to execute.
131      * @param sqlInput Input values for the command.
132      * @param sqlOutput The results of the command are put in this Hashtable.
133      * @return True, if no error occured.
134      */
135     public boolean executeSQLCommand(String command, Hashtable sqlInput, Hashtable sqlOutput)
136                                         throws IllegalArgumentException {
137         return executeSQLCommand(command, sqlInput, sqlOutput, false);
138     }
139 
140     /**
141      * Execute an sql command.
142      * @param command The command to execute.
143      * @param sqlInput Input values for the command.
144      * @param sqlOutput The results of the command are put in this Hashtable.
145      * @param mergeOutputIntoInput The output of the result is put into the
146      * sqlInput Hashtable.
147      * @return True, if no error occured.
148      */
149     public boolean executeSQLCommand(String command, Hashtable sqlInput, Hashtable sqlOutput, boolean mergeOutputIntoInput)
150                                         throws IllegalArgumentException {
151         if (sqlOutput == null) {
152             sqlOutput = new Hashtable();
153             // this has to be true, so the user of this method gets a return
154             mergeOutputIntoInput = true;
155             if (sqlInput == null) {
156                 System.out.println("sqlInput and sqlOutput are null: cannot return the results of the sql command.");
157                 return false;
158             }
159         }
160         Vector sqlCommand = new Vector();
161         sqlCommand.add("");
162         boolean update = false;
163         // synchronize all "$varname" occurences in the command string with
164         // values from input
165         StringTokenizer token = new StringTokenizer(command, " ");
166         while (token.hasMoreTokens()) {
167             String originalKey = token.nextToken();
168             // search for keys, beginning with "$"
169             int index = originalKey.indexOf('$');
170             if (index != -1) {
171                 // found key
172                 String pre = "";
173                 pre = originalKey.substring(0,index);
174                 // generate key: remove "$"
175                 String key = originalKey.substring(index+1);
176                 String post = "";
177                 // remove any endings the key might have
178                 while (key.endsWith(",") || key.endsWith("\'") ||
179                        key.endsWith(";") || key.endsWith(")") ||
180                        key.endsWith("\"")) {
181                     post = key.substring(key.length()-1) + post;
182                     key = key.substring(0, key.length()-1);
183                 }
184                 // look for key in the Hashtable
185                 if (sqlInput.containsKey(key)) {
186                     // is there a value for the key?
187                     Object in = sqlInput.get(key);
188                     if (in instanceof String[]) {
189                         // value is a String[]
190                         String[]vals = (String[])in;
191                         if (vals.length != sqlCommand.size() && sqlCommand.size() > 1) {
192                             // size of this array and previous array(s) does not match
193                             throw new IllegalArgumentException("The key '" + key + "' in command \n'"
194                                     + command + "'\n has not the same value count as the keys before.");
195                         }
196                         // build up the commands
197                         boolean addNewVals = (sqlCommand.size() == 1);
198                         for (int i=0; i<vals.length; i++) {
199                             String value = checkForQuotationMarks(vals[i]);
200                             // add the values
201                             if (addNewVals && i!=0) {
202                                 // all values until now were of type String, not String[], so now new values have to be added.
203                                 sqlCommand.add(i, (String)sqlCommand.get(0) + " " + pre + value + post);
204                             }
205                             else {
206                                 // we already have vals.length commands (or are at the first command), so just add.
207                                 sqlCommand.set(i, (String)sqlCommand.get(i) + " " + pre + value + post);
208                             }
209                         }
210                     }
211                     else {
212                         // value is a String: no other possibility
213                         String value = checkForQuotationMarks((String)sqlInput.get(key));
214                         for (int i=0; i<sqlCommand.size(); i++) {
215                             sqlCommand.set(i, (String)sqlCommand.get(i) + " " + pre + value + post);
216                         }
217                     }
218                 }
219                 else {
220                     // no input value found
221                     throw new IllegalArgumentException ("The key '" + key + "' in command \n'"
222                     + command + "'\n does not exist in the input values.");
223                 }
224             }
225             else {
226                 // token is not a key, just add it
227                 for (int i=0; i<sqlCommand.size(); i++)
228                     sqlCommand.set(i, (String)sqlCommand.get(i) + " " + originalKey);
229                 if (originalKey.equalsIgnoreCase("update") ||
230                                     originalKey.equalsIgnoreCase("delete") ||
231                                     originalKey.equalsIgnoreCase("insert")) {
232                     update = true;
233                 }
234 
235             }
236         }
237         for (int i=0;i<sqlCommand.size(); i++) {
238             execute((String)sqlCommand.get(i), sqlOutput, update);
239             // merge output with input
240             if (!update && mergeOutputIntoInput) {
241                 Enumeration keys = sqlOutput.keys();
242                 while(keys.hasMoreElements()) {
243                     String key = (String)keys.nextElement();
244                     String[]val = (String[])sqlOutput.get(key);
245                     if (val != null && val.length != 0) {
246                         if (val.length == 1)
247                             sqlInput.put(key, val[0]);
248                         else
249                             sqlInput.put(key, val);
250                     }
251                 }
252             }
253         }
254         if (!update && sqlOutput == null)
255             return false;
256         return true;
257     }
258 
259     /**
260      * Execute any SQL command.
261      * @param command The command.
262      * @param update If true, it is a update/alter command instead of an select
263      *          command
264      * @return A Hashtable with the result.
265      */
266     private void execute(String command, Hashtable output, boolean update) {
267         if (m_bDebug)
268             System.out.println("Debug - SQLExecution - execute Command: " + command);
269         try {
270             if (update) {
271                 // make an update
272                 mStatement.executeUpdate(command);
273             }
274             else {
275                 // make a select: collect the result
276                 ResultSet sqlResult = mStatement.executeQuery(command);
277                 ResultSetMetaData sqlRSMeta = sqlResult.getMetaData();
278                 int columnCount = sqlRSMeta.getColumnCount();
279                 String[] columnNames = new String[columnCount];
280                 int countRows = 0;
281                 boolean goThroughRowsTheFirstTime = true;
282                 for(int i=1; i<=columnCount; i++) {
283                     columnNames[i-1] = sqlRSMeta.getColumnName(i);
284                     // initialize output
285                     Vector v = new Vector();
286 
287                     sqlResult.beforeFirst();
288                     while (sqlResult.next()) {
289                         String value = sqlResult.getString(i);
290                         v.add(value);
291                         // the first time: count rows
292                         if (goThroughRowsTheFirstTime)
293                             countRows++;
294                     }
295                     // rows are counted
296                     if (goThroughRowsTheFirstTime)
297                         goThroughRowsTheFirstTime = false;
298 
299                     // put result in output Hashtable
300                     String[]s = new String[countRows];
301                     s = (String[])v.toArray(s);
302                     output.put(columnNames[i-1], s);
303                     if (m_bDebug) {
304                         if (i == 1) {
305                             System.out.print("Debug - SQLExecution - Command returns: ");
306                             System.out.print("row: " + columnNames[i-1] + "   vals: ");
307                         }
308                         for (int j=0; j<s.length; j++)
309                             System.out.print(s[j] + " ");
310                         if (i == columnCount - 1)
311                             System.out.println();
312                     }
313                 }
314             }
315         }
316         catch (java.sql.SQLException e) {
317             e.printStackTrace();
318         }
319     }
320 
321     /**
322      * Replace <"> with <''> in the value Strings, or the command will fail.
323      * @param checkString The String that is checked: a part of the command
324      * @return The String, cleared of all quotation marks.
325      */
326     private String checkForQuotationMarks(String checkString) {
327         String returnString = checkString;
328         int quotIndex = 0;
329         while ((quotIndex = returnString.indexOf('\"')) != -1) {
330             String firstHalf = returnString.substring(0, quotIndex);
331             String secondHalf = returnString.substring(quotIndex+1);
332             returnString = firstHalf + "\'\'" + secondHalf;
333         }
334         return returnString;
335     }
336 
337 }
338