Initial Document loading implementation, instead of libglom.
[online-glom:gwt-glom.git] / src / main / java / org / glom / web / server / database / DBAccess.java
1 /*
2  * Copyright (C) 2011 Openismus GmbH
3  *
4  * This file is part of GWT-Glom.
5  *
6  * GWT-Glom is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * GWT-Glom is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with GWT-Glom.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package org.glom.web.server.database;
21
22 import java.sql.Date;
23 import java.sql.ResultSet;
24 import java.sql.SQLException;
25 import java.sql.Time;
26 import java.text.DateFormat;
27 import java.util.ArrayList;
28 import java.util.Locale;
29
30 import org.glom.libglom.Field;
31 import org.glom.libglom.FieldVector;
32 import org.glom.libglom.LayoutFieldVector;
33 import org.glom.libglom.LayoutGroupVector;
34 import org.glom.libglom.LayoutItem;
35 import org.glom.libglom.LayoutItemVector;
36 import org.glom.libglom.LayoutItem_Field;
37 import org.glom.libglom.LayoutItem_Portal;
38 import org.glom.web.server.Log;
39 import org.glom.web.server.Utils;
40 import org.glom.web.shared.DataItem;
41 import org.glom.web.shared.libglom.Document;
42
43 import com.mchange.v2.c3p0.ComboPooledDataSource;
44
45 /**
46  *
47  */
48 abstract class DBAccess {
49         protected Document document;
50         protected String documentID;
51         protected String tableName;
52         protected ComboPooledDataSource cpds;
53
54         protected DBAccess(Document document, String documentID, ComboPooledDataSource cpds, String tableName) {
55                 this.document = document;
56                 this.documentID = documentID;
57                 this.cpds = cpds;
58                 this.tableName = tableName;
59         }
60
61         /*
62          * Converts data from a ResultSet to an ArrayList of DataItem array suitable for sending back to the client.
63          */
64         final protected ArrayList<DataItem[]> convertResultSetToDTO(int length, LayoutFieldVector layoutFields, ResultSet rs)
65                         throws SQLException {
66
67                 // get the data we've been asked for
68                 int rowCount = 0;
69                 ArrayList<DataItem[]> rowsList = new ArrayList<DataItem[]>();
70                 while (rs.next() && rowCount <= length) {
71                         int layoutFieldsSize = Utils.safeLongToInt(layoutFields.size());
72                         DataItem[] rowArray = new DataItem[layoutFieldsSize];
73                         for (int i = 0; i < layoutFieldsSize; i++) {
74                                 // make a new DataItem to set the text and colours
75                                 rowArray[i] = new DataItem();
76
77                                 // Convert the field value to a string based on the glom type. We're doing the formatting on the
78                                 // server side for now but it might be useful to move this to the client side.
79                                 LayoutItem_Field field = layoutFields.get(i);
80                                 switch (field.get_glom_type()) {
81                                 case TYPE_TEXT:
82                                         String text = rs.getString(i + 1);
83                                         rowArray[i].setText(text != null ? text : "");
84                                         break;
85                                 case TYPE_BOOLEAN:
86                                         rowArray[i].setBoolean(rs.getBoolean(i + 1));
87                                         break;
88                                 case TYPE_NUMERIC:
89                                         rowArray[i].setNumber(rs.getDouble(i + 1));
90                                         break;
91                                 case TYPE_DATE:
92                                         Date date = rs.getDate(i + 1);
93                                         if (date != null) {
94                                                 //TODO: Pass Date and Time types instead of converting to text here?
95                                                 //TODO: Use a 4-digit-year short form, somehow.
96                                                 DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.ROOT);
97                                                 rowArray[i].setText(dateFormat.format(date));
98                                         } else {
99                                                 rowArray[i].setText("");
100                                         }
101                                         break;
102                                 case TYPE_TIME:
103                                         Time time = rs.getTime(i + 1);
104                                         if (time != null) {
105                                                 DateFormat timeFormat = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.ROOT);
106                                                 rowArray[i].setText(timeFormat.format(time));
107                                         } else {
108                                                 rowArray[i].setText("");
109                                         }
110                                         break;
111                                 case TYPE_IMAGE:
112                                         byte[] image = rs.getBytes(i + 1);
113                                         if (image != null) {
114                                                 // TODO implement field TYPE_IMAGE
115                                                 rowArray[i].setText("Image (FIXME)");
116                                         } else {
117                                                 rowArray[i].setText("");
118                                         }
119                                         break;
120                                 case TYPE_INVALID:
121                                 default:
122                                         Log.warn(documentID, tableName, "Invalid LayoutItem Field type. Using empty string for value.");
123                                         rowArray[i].setText("");
124                                         break;
125                                 }
126                         }
127
128                         // add the row of DataItems to the ArrayList we're going to return and update the row count
129                         rowsList.add(rowArray);
130                         rowCount++;
131                 }
132
133                 return rowsList;
134         }
135
136         /*
137          * Gets a LayoutFieldVector to use when generating an SQL query.
138          */
139         protected LayoutFieldVector getFieldsToShowForSQLQuery(LayoutGroupVector layoutGroupVec) {
140                 LayoutFieldVector layoutFieldVector = new LayoutFieldVector();
141
142                 // We will show the fields that the document says we should:
143                 for (int i = 0; i < layoutGroupVec.size(); i++) {
144                         org.glom.libglom.LayoutGroup layoutGroup = layoutGroupVec.get(i);
145
146                         // satisfy the precondition of getDetailsLayoutGroup(String tableName, org.glom.libglom.LayoutGroup
147                         // libglomLayoutGroup)
148                         if (layoutGroup == null)
149                                 continue;
150
151                         // Get the fields:
152                         ArrayList<LayoutItem_Field> layoutItemFields = getFieldsToShowForSQLQueryAddGroup(layoutGroup);
153                         for (LayoutItem_Field layoutItem_Field : layoutItemFields) {
154                                 layoutFieldVector.add(layoutItem_Field);
155                         }
156                 }
157                 return layoutFieldVector;
158         }
159
160         /*
161          * Gets an ArrayList of LayoutItem_Field objects to use when generating an SQL query.
162          * 
163          * @precondition libglomLayoutGroup must not be null
164          */
165         private ArrayList<LayoutItem_Field> getFieldsToShowForSQLQueryAddGroup(
166                         org.glom.libglom.LayoutGroup libglomLayoutGroup) {
167
168                 ArrayList<LayoutItem_Field> layoutItemFields = new ArrayList<LayoutItem_Field>();
169                 LayoutItemVector items = libglomLayoutGroup.get_items();
170                 int numItems = Utils.safeLongToInt(items.size());
171                 for (int i = 0; i < numItems; i++) {
172                         LayoutItem layoutItem = items.get(i);
173
174                         LayoutItem_Field layoutItemField = LayoutItem_Field.cast_dynamic(layoutItem);
175                         if (layoutItemField != null) {
176                                 // the layoutItem is a LayoutItem_Field
177                                 FieldVector fields;
178                                 if (layoutItemField.get_has_relationship_name()) {
179                                         // layoutItemField is a field in a related table
180                                         fields = document.get_table_fields(layoutItemField.get_table_used(tableName));
181                                 } else {
182                                         // layoutItemField is a field in this table
183                                         fields = document.get_table_fields(tableName);
184                                 }
185
186                                 // set the layoutItemFeild with details from its Field in the document and
187                                 // add it to the list to be returned
188                                 for (int j = 0; j < fields.size(); j++) {
189                                         // check the names to see if they're the same
190                                         // this works because we're using the field list from the related table if necessary
191                                         if (layoutItemField.get_name().equals(fields.get(j).get_name())) {
192                                                 Field field = fields.get(j);
193                                                 if (field != null) {
194                                                         layoutItemField.set_full_field_details(field);
195                                                         layoutItemFields.add(layoutItemField);
196                                                 } else {
197                                                         Log.warn(document.get_database_title_original(), tableName, "LayoutItem_Field "
198                                                                         + layoutItemField.get_layout_display_name() + " not found in document field list.");
199                                                 }
200                                                 break;
201                                         }
202                                 }
203
204                         } else {
205                                 // the layoutItem is not a LayoutItem_Field
206                                 org.glom.libglom.LayoutGroup subLayoutGroup = org.glom.libglom.LayoutGroup.cast_dynamic(layoutItem);
207                                 if (subLayoutGroup != null) {
208                                         // the layoutItem is a LayoutGroup
209                                         LayoutItem_Portal layoutItemPortal = LayoutItem_Portal.cast_dynamic(layoutItem);
210                                         if (layoutItemPortal == null) {
211                                                 // The subGroup is not a LayoutItem_Portal.
212                                                 // We're ignoring portals because they are filled by means of a separate SQL query.
213                                                 layoutItemFields.addAll(getFieldsToShowForSQLQueryAddGroup(subLayoutGroup));
214                                         }
215                                 }
216                         }
217                 }
218                 return layoutItemFields;
219         }
220
221         /**
222          * Gets the primary key Field for the specified table name.
223          * 
224          * @param tableName
225          *            name of table to search for the primary key field
226          * @return primary key Field
227          */
228         protected Field getPrimaryKeyField(String tableName) {
229                 Field primaryKey = null;
230                 FieldVector fieldsVec = document.get_table_fields(tableName);
231                 for (int i = 0; i < Utils.safeLongToInt(fieldsVec.size()); i++) {
232                         Field field = fieldsVec.get(i);
233                         if (field.get_primary_key()) {
234                                 primaryKey = field;
235                                 break;
236                         }
237                 }
238                 return primaryKey;
239         }
240
241         /**
242          * Gets the primary key LayoutItem_Field for the specified table.
243          * 
244          * @param tableName
245          *            name of table to search for the primary key LayoutItem_Field
246          * @return primary key LayoutItem_Field
247          */
248         protected LayoutItem_Field getPrimaryKeyLayoutItemField(String tableName) {
249                 Field primaryKey = getPrimaryKeyField(tableName);
250
251                 LayoutItem_Field libglomLayoutItemField = new LayoutItem_Field();
252
253                 if (primaryKey != null) {
254                         libglomLayoutItemField.set_full_field_details(primaryKey);
255                 } else {
256                         Log.error(document.get_database_title_original(), this.tableName,
257                                         "A primary key was not found in the FieldVector for this table.");
258                 }
259
260                 return libglomLayoutItemField;
261         }
262
263         /*
264          * Find the LayoutItem_Portal for the related list name
265          */
266         final protected LayoutItem_Portal getPortal(String relationshipName) {
267
268                 LayoutGroupVector layoutGroupVec = document.get_data_layout_groups("details", tableName);
269                 // LayoutItem_Portal portal = null;
270                 for (int i = 0; i < layoutGroupVec.size(); i++) {
271                         org.glom.libglom.LayoutGroup layoutGroup = layoutGroupVec.get(i);
272                         LayoutItem_Portal portal = getPortal(relationshipName, layoutGroup);
273                         if (portal != null) {
274                                 return portal;
275                         }
276                 }
277
278                 // the LayoutItem_Portal with relationshipName was not found
279                 return null;
280         }
281
282         /*
283          * Recursive helper method.
284          */
285         final private LayoutItem_Portal getPortal(String relationshipName, org.glom.libglom.LayoutGroup layoutGroup) {
286
287                 if (relationshipName == null)
288                         return null;
289
290                 LayoutItemVector items = layoutGroup.get_items();
291                 for (int i = 0; i < items.size(); i++) {
292                         LayoutItem layoutItem = items.get(i);
293
294                         LayoutItem_Field layoutItemField = LayoutItem_Field.cast_dynamic(layoutItem);
295                         if (layoutItemField != null) {
296                                 // the layoutItem is a LayoutItem_Field
297                                 continue;
298
299                         } else {
300                                 // the layoutItem is not a LayoutItem_Field
301                                 org.glom.libglom.LayoutGroup subLayoutGroup = org.glom.libglom.LayoutGroup.cast_dynamic(layoutItem);
302                                 if (subLayoutGroup != null) {
303                                         // the layoutItem is a LayoutGroup
304                                         LayoutItem_Portal layoutItemPortal = LayoutItem_Portal.cast_dynamic(layoutItem);
305                                         if (layoutItemPortal != null) {
306                                                 // The subGroup is a LayoutItem_Protal
307                                                 if (relationshipName.equals(layoutItemPortal.get_relationship_name_used())) {
308                                                         // yey, we found it!
309                                                         return layoutItemPortal;
310                                                 }
311                                         } else {
312                                                 // The subGroup is not a LayoutItem_Portal.
313                                                 LayoutItem_Portal retval = getPortal(relationshipName, subLayoutGroup);
314                                                 if (retval != null) {
315                                                         return retval;
316                                                 }
317                                         }
318                                 }
319                         }
320                 }
321
322                 // the LayoutItem_Portal with relationshipName was not found
323                 return null;
324         }
325
326 }