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