SelfHosting test: Also try using a relationship.
[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.ResultSet;
23 import java.sql.ResultSetMetaData;
24 import java.sql.SQLException;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import org.glom.web.server.Log;
29 import org.glom.web.server.SqlUtils;
30 import org.glom.web.server.Utils;
31 import org.glom.web.server.libglom.Document;
32 import org.glom.web.shared.DataItem;
33 import org.glom.web.shared.TypedDataItem;
34 import org.glom.web.shared.libglom.Field;
35 import org.glom.web.shared.libglom.layout.LayoutGroup;
36 import org.glom.web.shared.libglom.layout.LayoutItem;
37 import org.glom.web.shared.libglom.layout.LayoutItemField;
38 import org.glom.web.shared.libglom.layout.LayoutItemPortal;
39
40 import com.mchange.v2.c3p0.ComboPooledDataSource;
41
42 /**
43  *
44  */
45 public abstract class DBAccess {
46         public Document document;
47         protected String documentID;
48         protected String tableName;
49         protected ComboPooledDataSource cpds;
50
51         protected DBAccess(final Document document, final String documentID, final ComboPooledDataSource cpds,
52                         final 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(final int length,
63                         final List<LayoutItemField> layoutFields, final TypedDataItem primaryKeyValue, final ResultSet rs) throws SQLException {
64
65                 final ResultSetMetaData rsMetaData = rs.getMetaData();
66                 final int rsColumnscount = rsMetaData.getColumnCount();
67
68                 // get the data we've been asked for
69                 int rowCount = 0;
70                 final ArrayList<DataItem[]> rowsList = new ArrayList<DataItem[]>();
71                 while (rs.next() && rowCount <= length) {
72                         final int layoutFieldsSize = Utils.safeLongToInt(layoutFields.size());
73                         final DataItem[] rowArray = new DataItem[layoutFieldsSize];
74                         for (int i = 0; i < layoutFieldsSize; i++) {
75                                 // make a new DataItem to set the text and colors
76                                 final DataItem dataItem = new DataItem();
77
78                                 final LayoutItemField field = layoutFields.get(i);
79
80                                 if (i >= rsColumnscount) {
81                                         Log.error("convertResultSetToDTO(): index i=" + i + "+1 (field=" + field.getName()
82                                                         + " is out of range for the ResultSet. Using empty string for value.");
83                                         dataItem.setText("");
84                                         continue;
85                                 }
86
87                                 final int rsIndex = i + 1; // Because java.sql.ResultSet is 1-indexed, for some reason.
88
89                                 // Convert the field value to a string based on the glom type. We're doing the formatting on the
90                                 // server side for now but it might be useful to move this to the client side.
91                                 SqlUtils.fillDataItemFromResultSet(dataItem, field, rsIndex, rs, documentID, tableName, primaryKeyValue);
92                                 
93                                 rowArray[i] = dataItem;
94                         }
95
96                         // add the row of DataItems to the ArrayList we're going to return and update the row count
97                         rowsList.add(rowArray);
98                         rowCount++;
99                 }
100
101                 return rowsList;
102         }
103
104         /*
105          * Gets a list to use when generating an SQL query.
106          */
107         protected List<LayoutItemField> getFieldsToShowForSQLQuery(final List<LayoutGroup> layoutGroupVec) {
108                 final List<LayoutItemField> listLayoutFIelds = new ArrayList<LayoutItemField>();
109
110                 // We will show the fields that the document says we should:
111                 for (int i = 0; i < layoutGroupVec.size(); i++) {
112                         final LayoutGroup layoutGroup = layoutGroupVec.get(i);
113
114                         // satisfy the precondition of getDetailsLayoutGroup(String tableName, LayoutGroup
115                         // libglomLayoutGroup)
116                         if (layoutGroup == null) {
117                                 continue;
118                         }
119
120                         // Get the fields:
121                         final ArrayList<LayoutItemField> layoutItemFields = getFieldsToShowForSQLQueryAddGroup(layoutGroup);
122                         for (final LayoutItemField layoutItem_Field : layoutItemFields) {
123                                 listLayoutFIelds.add(layoutItem_Field);
124                         }
125                 }
126                 return listLayoutFIelds;
127         }
128
129         /*
130          * Gets an ArrayList of LayoutItem_Field objects to use when generating an SQL query.
131          * 
132          * @precondition libglomLayoutGroup must not be null
133          */
134         private ArrayList<LayoutItemField> getFieldsToShowForSQLQueryAddGroup(final LayoutGroup libglomLayoutGroup) {
135
136                 final ArrayList<LayoutItemField> layoutItemFields = new ArrayList<LayoutItemField>();
137                 final List<LayoutItem> items = libglomLayoutGroup.getItems();
138                 final int numItems = Utils.safeLongToInt(items.size());
139                 for (int i = 0; i < numItems; i++) {
140                         final LayoutItem layoutItem = items.get(i);
141
142                         if (layoutItem instanceof LayoutItemField) {
143                                 final LayoutItemField layoutItemField = (LayoutItemField) layoutItem;
144                                 // the layoutItem is a LayoutItem_Field
145
146                                 // Make sure that it has full field details:
147                                 // TODO: Is this necessary?
148                                 String tableNameToUse = tableName;
149                                 if (layoutItemField.getHasRelationshipName()) {
150                                         tableNameToUse = layoutItemField.getTableUsed(tableName);
151                                 }
152
153                                 final Field field = document.getField(tableNameToUse, layoutItemField.getName());
154                                 if (field != null) {
155                                         layoutItemField.setFullFieldDetails(field);
156                                 } else {
157                                         Log.warn(document.getDatabaseTitleOriginal(), tableName,
158                                                         "LayoutItem_Field " + layoutItemField.getLayoutDisplayName()
159                                                                         + " not found in document field list.");
160                                 }
161
162                                 // Add it to the list:
163                                 layoutItemFields.add(layoutItemField);
164                         } else if (layoutItem instanceof LayoutGroup) {
165                                 final LayoutGroup subLayoutGroup = (LayoutGroup) layoutItem;
166
167                                 if (!(subLayoutGroup instanceof LayoutItemPortal)) {
168                                         // The subGroup is not a LayoutItemPortal.
169                                         // We're ignoring portals because they are filled by means of a separate SQL query.
170                                         layoutItemFields.addAll(getFieldsToShowForSQLQueryAddGroup(subLayoutGroup));
171                                 }
172                         }
173                 }
174                 return layoutItemFields;
175         }
176
177         /**
178          * Gets the primary key LayoutItem_Field for the specified table.
179          * 
180          * @param tableName
181          *            name of table to search for the primary key LayoutItem_Field
182          * @return primary key LayoutItem_Field
183          */
184         protected LayoutItemField getPrimaryKeyLayoutItemField(final String tableName) {
185                 final Field primaryKey = document.getTablePrimaryKeyField(tableName);
186
187                 final LayoutItemField libglomLayoutItemField = new LayoutItemField();
188
189                 if (primaryKey != null) {
190                         libglomLayoutItemField.setName(primaryKey.getName());
191                         libglomLayoutItemField.setFullFieldDetails(primaryKey);
192                 } else {
193                         Log.error(document.getDatabaseTitleOriginal(), this.tableName,
194                                         "A primary key was not found in the FieldVector for this table.");
195                 }
196
197                 return libglomLayoutItemField;
198         }
199
200 }