2 * Copyright (C) 2011 Openismus GmbH
4 * This file is part of GWT-Glom.
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.
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
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/>.
20 package org.glom.web.server.database;
22 import java.sql.Connection;
23 import java.sql.ResultSet;
24 import java.sql.ResultSetMetaData;
25 import java.sql.SQLException;
26 import java.sql.Statement;
28 import org.apache.commons.lang3.StringUtils;
29 import org.glom.libglom.LayoutFieldVector;
30 import org.glom.libglom.LayoutItem_Field;
31 import org.glom.libglom.LayoutItem_Portal;
32 import org.glom.libglom.Value;
33 import org.glom.web.server.Log;
34 import org.glom.web.server.SqlUtils;
35 import org.glom.web.server.Utils;
36 import org.glom.web.shared.NavigationRecord;
37 import org.glom.web.shared.TypedDataItem;
38 import org.glom.web.shared.libglom.Document;
39 import org.glom.web.shared.libglom.Field;
40 import org.glom.web.shared.libglom.Field.glom_field_type;
42 import com.mchange.v2.c3p0.ComboPooledDataSource;
47 public class RelatedListNavigation extends DBAccess {
49 private LayoutItem_Portal portal;
51 public RelatedListNavigation(final Document document, final String documentID, final ComboPooledDataSource cpds,
52 final String tableName, final String relationshipName) {
53 super(document, documentID, cpds, tableName);
55 final LayoutItem_Portal portal = getPortal(relationshipName);
57 Log.error(documentID, tableName, "Couldn't find LayoutItem_Portal \"" + relationshipName + "\" in table \""
58 + tableName + "\". " + "Cannot retrive data for the related list.");
66 * Gets a NavigationRecord for the related list given the primaryKeyValue.
68 * This code was ported from Glom: Box_Data_Portal::get_suitable_record_to_view_details()
70 public NavigationRecord getNavigationRecord(final TypedDataItem primaryKeyValue) {
73 Log.error(documentID, tableName,
74 "The related list navigation cannot be determined because the LayoutItem_Portal has not been found.");
78 final StringBuffer navigationTableNameSB = new StringBuffer();
79 final LayoutItem_Field navigationRelationshipItem = new LayoutItem_Field();
80 //TODO: //portal.get_suitable_table_to_view_details(navigationTableNameSB, navigationRelationshipItem, document);
82 final String navigationTableName = navigationTableNameSB.toString();
83 if (StringUtils.isEmpty(navigationTableName)) {
84 Log.error(documentID, tableName,
85 "The related list navigation cannot cannot be determined because the navigation table name is empty.");
89 // Get the primary key of that table:
90 final Field navigationTablePrimaryKey = getPrimaryKeyField(navigationTableName);
92 // Build a layout item to get the field's value:
93 navigationRelationshipItem.set_full_field_details(navigationTablePrimaryKey);
95 // Get the value of the navigation related primary key:
96 final LayoutFieldVector fieldsToGet = new LayoutFieldVector();
97 fieldsToGet.add(navigationRelationshipItem);
99 // For instance "invoice_line_id" if this is a portal to an "invoice_lines" table:
100 final String relatedTableName = portal.get_table_used("" /* not relevant */);
101 final Field primaryKeyField = getPrimaryKeyField(relatedTableName);
103 final NavigationRecord navigationRecord = new NavigationRecord();
105 Connection conn = null;
109 // Setup the JDBC driver and get the query.
110 conn = cpds.getConnection();
111 st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
113 final Value gdaPrimaryKeyValue = Utils.getGlomTypeGdaValueForTypedDataItem(documentID, tableName,
114 primaryKeyField.get_glom_type(), primaryKeyValue);
116 // Only create the query if we've created a Gda Value from the DataItem.
117 if (gdaPrimaryKeyValue != null) {
119 query = SqlUtils.build_sql_select_with_key(conn, relatedTableName, fieldsToGet, primaryKeyField,
122 rs = st.executeQuery(query);
124 // Set the output parameters:
125 navigationRecord.setTableName(navigationTableName);
128 final TypedDataItem navigationTablePrimaryKeyValue = new TypedDataItem();
129 final ResultSetMetaData rsMetaData = rs.getMetaData();
130 final int queryReturnValueType = rsMetaData.getColumnType(1);
131 switch (navigationTablePrimaryKey.get_glom_type()) {
133 if (queryReturnValueType == java.sql.Types.NUMERIC) {
134 navigationTablePrimaryKeyValue.setNumber(rs.getDouble(1));
136 logNavigationTablePrimaryKeyTypeMismatchError(Field.glom_field_type.TYPE_NUMERIC,
137 rsMetaData.getColumnTypeName(1));
141 if (queryReturnValueType == java.sql.Types.VARCHAR) {
142 navigationTablePrimaryKeyValue.setText(rs.getString(1));
144 logNavigationTablePrimaryKeyTypeMismatchError(Field.glom_field_type.TYPE_TEXT,
145 rsMetaData.getColumnTypeName(1));
149 Log.error(documentID, tableName, "Unsupported java.sql.Type: " + rsMetaData.getColumnTypeName(1));
150 Log.error(documentID, tableName,
151 "The navigation table primary key value will not be created. This is a bug.");
155 // The value is empty when there there is no record to match the key in the related table:
156 // For instance, if an invoice lines record mentions a product id, but the product does not exist in the
158 if (navigationTablePrimaryKeyValue.isEmpty()) {
159 Log.info(documentID, tableName, "SQL query returned empty primary key for navigation to the "
160 + navigationTableName + "table. Navigation may not work correctly");
161 navigationRecord.setPrimaryKeyValue(null);
163 navigationRecord.setPrimaryKeyValue(navigationTablePrimaryKeyValue);
166 } catch (final SQLException e) {
167 Log.error(documentID, tableName, "Error executing database query: " + query, e);
168 // TODO: somehow notify user of problem
171 // cleanup everything that has been used
179 } catch (final Exception e) {
180 Log.error(documentID, tableName,
181 "Error closing database resources. Subsequent database queries may not work.", e);
185 return navigationRecord;
188 private void logNavigationTablePrimaryKeyTypeMismatchError(final glom_field_type glomType,
189 final String queryReturnValueTypeName) {
190 Log.error(documentID, tableName, "The expected type from the Glom document: " + glomType
191 + " doesn't match the type returned by the SQL query: " + queryReturnValueTypeName + ".");
192 Log.error(documentID, tableName, "The navigation table primary key value will not be created. This is a bug.");