Add field colouring to details view.
[online-glom:gwt-glom.git] / src / main / java / org / glom / web / server / ConfiguredDocument.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;
21
22 import java.beans.PropertyVetoException;
23 import java.sql.Connection;
24 import java.sql.SQLException;
25 import java.util.ArrayList;
26
27 import org.glom.libglom.Document;
28 import org.glom.libglom.Field;
29 import org.glom.libglom.FieldFormatting;
30 import org.glom.libglom.FieldVector;
31 import org.glom.libglom.LayoutGroupVector;
32 import org.glom.libglom.LayoutItemVector;
33 import org.glom.libglom.LayoutItem_Field;
34 import org.glom.libglom.LayoutItem_Portal;
35 import org.glom.libglom.NumericFormat;
36 import org.glom.libglom.Relationship;
37 import org.glom.libglom.StringVector;
38 import org.glom.web.server.database.DetailsDBAccess;
39 import org.glom.web.server.database.ListViewDBAccess;
40 import org.glom.web.server.database.RelatedListDBAccess;
41 import org.glom.web.shared.DataItem;
42 import org.glom.web.shared.DocumentInfo;
43 import org.glom.web.shared.GlomNumericFormat;
44 import org.glom.web.shared.layout.Formatting;
45 import org.glom.web.shared.layout.LayoutGroup;
46 import org.glom.web.shared.layout.LayoutItemField;
47 import org.glom.web.shared.layout.LayoutItemPortal;
48
49 import com.mchange.v2.c3p0.ComboPooledDataSource;
50
51 /**
52  * A class to hold configuration information for a given Glom document. This class is used to retrieve layout
53  * information from libglom and data from the underlying PostgreSQL database.
54  * 
55  * @author Ben Konrath <ben@bagu.org>
56  * 
57  */
58 final class ConfiguredDocument {
59
60         private Document document;
61         private ComboPooledDataSource cpds;
62         private boolean authenticated = false;
63         private String documentID;
64
65         @SuppressWarnings("unused")
66         private ConfiguredDocument() {
67                 // disable default constructor
68         }
69
70         ConfiguredDocument(Document document) throws PropertyVetoException {
71
72                 // load the jdbc driver
73                 cpds = new ComboPooledDataSource();
74
75                 // We don't support sqlite or self-hosting yet.
76                 if (document.get_hosting_mode() != Document.HostingMode.HOSTING_MODE_POSTGRES_CENTRAL) {
77                         Log.fatal("Error configuring the database connection." + " Only central PostgreSQL hosting is supported.");
78                         // FIXME: Throw exception?
79                 }
80
81                 try {
82                         cpds.setDriverClass("org.postgresql.Driver");
83                 } catch (PropertyVetoException e) {
84                         Log.fatal("Error loading the PostgreSQL JDBC driver."
85                                         + " Is the PostgreSQL JDBC jar available to the servlet?", e);
86                         throw e;
87                 }
88
89                 // setup the JDBC driver for the current glom document
90                 cpds.setJdbcUrl("jdbc:postgresql://" + document.get_connection_server() + ":" + document.get_connection_port()
91                                 + "/" + document.get_connection_database());
92
93                 this.document = document;
94         }
95
96         /**
97          * Sets the username and password for the database associated with the Glom document.
98          * 
99          * @return true if the username and password works, false otherwise
100          */
101         boolean setUsernameAndPassword(String username, String password) throws SQLException {
102                 cpds.setUser(username);
103                 cpds.setPassword(password);
104
105                 int acquireRetryAttempts = cpds.getAcquireRetryAttempts();
106                 cpds.setAcquireRetryAttempts(1);
107                 Connection conn = null;
108                 try {
109                         // FIXME find a better way to check authentication
110                         // it's possible that the connection could be failing for another reason
111                         conn = cpds.getConnection();
112                         authenticated = true;
113                 } catch (SQLException e) {
114                         Log.info(Utils.getFileName(document.get_file_uri()), e.getMessage());
115                         Log.info(Utils.getFileName(document.get_file_uri()),
116                                         "Connection Failed. Maybe the username or password is not correct.");
117                         authenticated = false;
118                 } finally {
119                         if (conn != null)
120                                 conn.close();
121                         cpds.setAcquireRetryAttempts(acquireRetryAttempts);
122                 }
123                 return authenticated;
124         }
125
126         Document getDocument() {
127                 return document;
128         }
129
130         ComboPooledDataSource getCpds() {
131                 return cpds;
132         }
133
134         boolean isAuthenticated() {
135                 return authenticated;
136         }
137
138         String getDocumentID() {
139                 return documentID;
140         }
141
142         void setDocumentID(String documentID) {
143                 this.documentID = documentID;
144         }
145
146         /**
147          * @return
148          */
149         DocumentInfo getDocumentInfo() {
150                 DocumentInfo documentInfo = new DocumentInfo();
151
152                 // get arrays of table names and titles, and find the default table index
153                 StringVector tablesVec = document.get_table_names();
154
155                 int numTables = Utils.safeLongToInt(tablesVec.size());
156                 // we don't know how many tables will be hidden so we'll use half of the number of tables for the default size
157                 // of the ArrayList
158                 ArrayList<String> tableNames = new ArrayList<String>(numTables / 2);
159                 ArrayList<String> tableTitles = new ArrayList<String>(numTables / 2);
160                 boolean foundDefaultTable = false;
161                 int visibleIndex = 0;
162                 for (int i = 0; i < numTables; i++) {
163                         String tableName = tablesVec.get(i);
164                         if (!document.get_table_is_hidden(tableName)) {
165                                 tableNames.add(tableName);
166                                 // JNI is "expensive", the comparison will only be called if we haven't already found the default table
167                                 if (!foundDefaultTable && tableName.equals(document.get_default_table())) {
168                                         documentInfo.setDefaultTableIndex(visibleIndex);
169                                         foundDefaultTable = true;
170                                 }
171                                 tableTitles.add(document.get_table_title(tableName));
172                                 visibleIndex++;
173                         }
174                 }
175
176                 // set everything we need
177                 documentInfo.setTableNames(tableNames);
178                 documentInfo.setTableTitles(tableTitles);
179                 documentInfo.setTitle(document.get_database_title());
180
181                 return documentInfo;
182         }
183
184         /*
185          * Gets the layout group for the list view using the defined layout list in the document or the table fields if
186          * there's no defined layout group for the list view.
187          */
188         private org.glom.libglom.LayoutGroup getValidListViewLayoutGroup(String tableName) {
189
190                 LayoutGroupVector layoutGroupVec = document.get_data_layout_groups("list", tableName);
191
192                 int listViewLayoutGroupSize = Utils.safeLongToInt(layoutGroupVec.size());
193                 org.glom.libglom.LayoutGroup libglomLayoutGroup = null;
194                 if (listViewLayoutGroupSize > 0) {
195                         // a list layout group is defined; we can use the first group as the list
196                         if (listViewLayoutGroupSize > 1)
197                                 Log.warn(documentID, tableName, "The size of the list layout group is greater than 1. "
198                                                 + "Attempting to use the first item for the layout list view.");
199
200                         libglomLayoutGroup = layoutGroupVec.get(0);
201                 } else {
202                         // a list layout group is *not* defined; we are going make a libglom layout group from the list of fields
203                         Log.info(documentID, tableName,
204                                         "A list layout is not defined for this table. Displaying a list layout based on the field list.");
205
206                         FieldVector fieldsVec = document.get_table_fields(tableName);
207                         libglomLayoutGroup = new org.glom.libglom.LayoutGroup();
208                         for (int i = 0; i < fieldsVec.size(); i++) {
209                                 Field field = fieldsVec.get(i);
210                                 LayoutItem_Field layoutItemField = new LayoutItem_Field();
211                                 layoutItemField.set_full_field_details(field);
212                                 libglomLayoutGroup.add_item(layoutItemField);
213                         }
214                 }
215
216                 return libglomLayoutGroup;
217         }
218
219         ArrayList<DataItem[]> getListViewData(String tableName, int start, int length, boolean useSortClause,
220                         int sortColumnIndex, boolean isAscending) {
221                 // Validate the table name.
222                 tableName = getTableNameToUse(tableName);
223
224                 // Get the libglom LayoutGroup that represents the list view.
225                 org.glom.libglom.LayoutGroup libglomLayoutGroup = getValidListViewLayoutGroup(tableName);
226
227                 // Create a database access object for the list view.
228                 ListViewDBAccess listViewDBAccess = new ListViewDBAccess(document, documentID, cpds, tableName,
229                                 libglomLayoutGroup);
230
231                 // Return the data.
232                 return listViewDBAccess.getData(start, length, useSortClause, sortColumnIndex, isAscending);
233         }
234
235         DataItem[] getDetailsData(String tableName, String primaryKeyValue) {
236                 // Validate the table name.
237                 tableName = getTableNameToUse(tableName);
238
239                 DetailsDBAccess detailsDBAccess = new DetailsDBAccess(document, documentID, cpds, tableName);
240
241                 return detailsDBAccess.getData(primaryKeyValue);
242         }
243
244         ArrayList<DataItem[]> getRelatedListData(String tableName, String relationshipName, String foreignKeyValue,
245                         int start, int length, boolean useSortClause, int sortColumnIndex, boolean isAscending) {
246                 // Validate the table name.
247                 tableName = getTableNameToUse(tableName);
248
249                 // Create a database access object for the related list
250                 RelatedListDBAccess relatedListDBAccess = new RelatedListDBAccess(document, documentID, cpds, tableName,
251                                 relationshipName);
252
253                 // Return the data
254                 return relatedListDBAccess.getData(start, length, foreignKeyValue, useSortClause, sortColumnIndex, isAscending);
255         }
256
257         ArrayList<LayoutGroup> getDetailsLayoutGroup(String tableName) {
258                 // Validate the table name.
259                 tableName = getTableNameToUse(tableName);
260
261                 // Get the details layout group information for each LayoutGroup in the LayoutGroupVector
262                 LayoutGroupVector layoutGroupVec = document.get_data_layout_groups("details", tableName);
263                 ArrayList<LayoutGroup> layoutGroups = new ArrayList<LayoutGroup>();
264                 for (int i = 0; i < layoutGroupVec.size(); i++) {
265                         org.glom.libglom.LayoutGroup libglomLayoutGroup = layoutGroupVec.get(i);
266
267                         // satisfy the precondition of getDetailsLayoutGroup(String, org.glom.libglom.LayoutGroup)
268                         if (libglomLayoutGroup == null)
269                                 continue;
270
271                         layoutGroups.add(getDetailsLayoutGroup(tableName, libglomLayoutGroup));
272                 }
273
274                 return layoutGroups;
275         }
276
277         LayoutGroup getListViewLayoutGroup(String tableName) {
278                 // Validate the table name.
279                 tableName = getTableNameToUse(tableName);
280
281                 org.glom.libglom.LayoutGroup libglomLayoutGroup = getValidListViewLayoutGroup(tableName);
282
283                 return getListLayoutGroup(tableName, libglomLayoutGroup);
284         }
285
286         /*
287          * Gets the expected row count for a related list.
288          */
289         int getRelatedListRowCount(String tableName, String relationshipName, String foreignKeyValue) {
290                 // Validate the table name.
291                 tableName = getTableNameToUse(tableName);
292
293                 // Create a database access object for the related list
294                 RelatedListDBAccess relatedListDBAccess = new RelatedListDBAccess(document, documentID, cpds, tableName,
295                                 relationshipName);
296
297                 // Return the row count
298                 return relatedListDBAccess.getExpectedResultSize(foreignKeyValue);
299         }
300
301         /*
302          * Gets a LayoutGroup DTO for the given table name and libglom LayoutGroup. This method can be used for the main
303          * list view table and for the related list table.
304          */
305         private LayoutGroup getListLayoutGroup(String tableName, org.glom.libglom.LayoutGroup libglomLayoutGroup) {
306                 LayoutGroup layoutGroup = new LayoutGroup();
307                 int primaryKeyIndex = -1;
308
309                 // look at each child item
310                 LayoutItemVector layoutItemsVec = libglomLayoutGroup.get_items();
311                 int numItems = Utils.safeLongToInt(layoutItemsVec.size());
312                 for (int i = 0; i < numItems; i++) {
313                         org.glom.libglom.LayoutItem libglomLayoutItem = layoutItemsVec.get(i);
314
315                         // TODO add support for other LayoutItems (Text, Image, Button etc.)
316                         LayoutItem_Field libglomLayoutItemField = LayoutItem_Field.cast_dynamic(libglomLayoutItem);
317                         if (libglomLayoutItemField != null) {
318                                 layoutGroup.addItem(convertToGWTGlomLayoutItemField(libglomLayoutItemField, true));
319                                 Field field = libglomLayoutItemField.get_full_field_details();
320                                 if (field.get_primary_key())
321                                         primaryKeyIndex = i;
322                         } else {
323                                 Log.info(documentID, tableName,
324                                                 "Ignoring unknown list LayoutItem of type " + libglomLayoutItem.get_part_type_name() + ".");
325                                 continue;
326                         }
327                 }
328
329                 // set the expected result size for list view tables
330                 LayoutItem_Portal libglomLayoutItemPortal = LayoutItem_Portal.cast_dynamic(libglomLayoutGroup);
331                 if (libglomLayoutItemPortal == null) {
332                         // libglomLayoutGroup is a list view
333                         ListViewDBAccess listViewDBAccess = new ListViewDBAccess(document, documentID, cpds, tableName,
334                                         libglomLayoutGroup);
335                         layoutGroup.setExpectedResultSize(listViewDBAccess.getExpectedResultSize());
336                 }
337
338                 // Set the primary key index for the table
339                 if (primaryKeyIndex < 0) {
340                         // Add a LayoutItemField for the primary key to the end of the item list in the LayoutGroup because it
341                         // doesn't already contain a primary key.
342                         Field primaryKey = null;
343                         FieldVector fieldsVec = document.get_table_fields(tableName);
344                         for (int i = 0; i < Utils.safeLongToInt(fieldsVec.size()); i++) {
345                                 Field field = fieldsVec.get(i);
346                                 if (field.get_primary_key()) {
347                                         primaryKey = field;
348                                         break;
349                                 }
350                         }
351                         if (primaryKey != null) {
352                                 LayoutItem_Field libglomLayoutItemField = new LayoutItem_Field();
353                                 libglomLayoutItemField.set_full_field_details(primaryKey);
354                                 layoutGroup.addItem(convertToGWTGlomLayoutItemField(libglomLayoutItemField, false));
355                                 layoutGroup.setPrimaryKeyIndex(layoutGroup.getItems().size() - 1);
356                                 layoutGroup.setHiddenPrimaryKey(true);
357                         } else {
358                                 Log.error(document.get_database_title(), tableName,
359                                                 "A primary key was not found in the FieldVector for this table. Navigation buttons will not work.");
360                         }
361
362                 } else {
363                         layoutGroup.setPrimaryKeyIndex(primaryKeyIndex);
364                 }
365
366                 layoutGroup.setTableName(tableName);
367
368                 return layoutGroup;
369         }
370
371         /*
372          * Gets a recursively defined Details LayoutGroup DTO for the specified libglom LayoutGroup object. This is used for
373          * getting layout information for the details view.
374          * 
375          * @param documentID Glom document identifier
376          * 
377          * @param tableName table name in the specified Glom document
378          * 
379          * @param libglomLayoutGroup libglom LayoutGroup to convert
380          * 
381          * @precondition libglomLayoutGroup must not be null
382          * 
383          * @return {@link LayoutGroup} object that represents the layout for the specified {@link
384          * org.glom.libglom.LayoutGroup}
385          */
386         private LayoutGroup getDetailsLayoutGroup(String tableName, org.glom.libglom.LayoutGroup libglomLayoutGroup) {
387                 LayoutGroup layoutGroup = new LayoutGroup();
388                 layoutGroup.setColumnCount(Utils.safeLongToInt(libglomLayoutGroup.get_columns_count()));
389                 layoutGroup.setTitle(libglomLayoutGroup.get_title());
390
391                 // look at each child item
392                 LayoutItemVector layoutItemsVec = libglomLayoutGroup.get_items();
393                 for (int i = 0; i < layoutItemsVec.size(); i++) {
394                         org.glom.libglom.LayoutItem libglomLayoutItem = layoutItemsVec.get(i);
395
396                         // just a safety check
397                         if (libglomLayoutItem == null)
398                                 continue;
399
400                         org.glom.web.shared.layout.LayoutItem layoutItem = null;
401                         org.glom.libglom.LayoutGroup group = org.glom.libglom.LayoutGroup.cast_dynamic(libglomLayoutItem);
402                         if (group != null) {
403                                 // libglomLayoutItem is a LayoutGroup
404                                 LayoutItem_Portal libglomLayoutItemPortal = LayoutItem_Portal.cast_dynamic(group);
405                                 if (libglomLayoutItemPortal != null) {
406                                         // group is a LayoutItemPortal
407                                         LayoutItemPortal layoutItemPortal = new LayoutItemPortal();
408                                         Relationship relationship = libglomLayoutItemPortal.get_relationship();
409                                         if (relationship != null) {
410                                                 layoutItemPortal.setNavigationType(convertToGWTGlomNavigationType(libglomLayoutItemPortal
411                                                                 .get_navigation_type()));
412
413                                                 layoutItemPortal.setTitle(libglomLayoutItemPortal.get_title_used("")); // parent title not
414                                                                                                                                                                                                 // relevant
415                                                 LayoutGroup tempLayoutGroup = getListLayoutGroup(tableName, libglomLayoutItemPortal);
416                                                 for (org.glom.web.shared.layout.LayoutItem item : tempLayoutGroup.getItems()) {
417                                                         // TODO EDITING If the relationship does not allow editing, then mark all these fields as
418                                                         // non-editable. Check relationship.get_allow_edit() to see if it's editable.
419                                                         layoutItemPortal.addItem(item);
420                                                 }
421                                                 layoutItemPortal.setPrimaryKeyIndex(tempLayoutGroup.getPrimaryKeyIndex());
422                                                 layoutItemPortal.setHiddenPrimaryKey(tempLayoutGroup.hasHiddenPrimaryKey());
423                                                 layoutItemPortal.setName(libglomLayoutItemPortal.get_relationship_name_used());
424                                                 layoutItemPortal.setTableName(relationship.get_from_table());
425                                                 layoutItemPortal.setFromField(relationship.get_from_field());
426                                         }
427
428                                         // Note: empty layoutItemPortal used if relationship is null
429                                         layoutItem = layoutItemPortal;
430
431                                 } else {
432                                         // group is *not* a LayoutItemPortal//
433                                         // recurse into child groups
434                                         layoutItem = getDetailsLayoutGroup(tableName, group);
435                                 }
436                         } else {
437                                 // libglomLayoutItem is *not* a LayoutGroup
438                                 // create LayoutItem DTOs based on the the libglom type
439                                 // TODO add support for other LayoutItems (Text, Image, Button etc.)
440                                 LayoutItem_Field libglomLayoutItemField = LayoutItem_Field.cast_dynamic(libglomLayoutItem);
441                                 if (libglomLayoutItemField != null) {
442                                         layoutItem = convertToGWTGlomLayoutItemField(libglomLayoutItemField, true);
443                                 } else {
444                                         Log.info(documentID, tableName,
445                                                         "Ignoring unknown details LayoutItem of type " + libglomLayoutItem.get_part_type_name()
446                                                                         + ".");
447                                         continue;
448                                 }
449                         }
450
451                         layoutGroup.addItem(layoutItem);
452                 }
453
454                 return layoutGroup;
455         }
456
457         private GlomNumericFormat convertNumbericFormat(NumericFormat libglomNumericFormat) {
458                 GlomNumericFormat gnf = new GlomNumericFormat();
459                 gnf.setUseAltForegroundColourForNegatives(libglomNumericFormat.getM_alt_foreground_color_for_negatives());
460                 gnf.setCurrencyCode(libglomNumericFormat.getM_currency_symbol());
461                 gnf.setDecimalPlaces(Utils.safeLongToInt(libglomNumericFormat.getM_decimal_places()));
462                 gnf.setDecimalPlacesRestricted(libglomNumericFormat.getM_decimal_places_restricted());
463                 gnf.setUseThousandsSeparator(libglomNumericFormat.getM_use_thousands_separator());
464                 return gnf;
465         }
466
467         private Formatting convertFormatting(FieldFormatting libglomFormatting) {
468                 Formatting formatting = new Formatting();
469
470                 // horizontal alignment
471                 Formatting.HorizontalAlignment horizontalAlignment;
472                 switch (libglomFormatting.get_horizontal_alignment()) {
473                 case HORIZONTAL_ALIGNMENT_LEFT:
474                         horizontalAlignment = Formatting.HorizontalAlignment.HORIZONTAL_ALIGNMENT_LEFT;
475                 case HORIZONTAL_ALIGNMENT_RIGHT:
476                         horizontalAlignment = Formatting.HorizontalAlignment.HORIZONTAL_ALIGNMENT_RIGHT;
477                 case HORIZONTAL_ALIGNMENT_AUTO:
478                 default:
479                         horizontalAlignment = Formatting.HorizontalAlignment.HORIZONTAL_ALIGNMENT_AUTO;
480                 }
481                 formatting.setHorizontalAlignment(horizontalAlignment);
482
483                 // text colour
484                 String foregroundColour = libglomFormatting.get_text_format_color_foreground();
485                 if (foregroundColour != null && !foregroundColour.isEmpty())
486                         formatting.setTextFormatColourForeground(convertGdkColorToHtmlColour(foregroundColour));
487                 String backgroundColour = libglomFormatting.get_text_format_color_background();
488                 if (backgroundColour != null && !backgroundColour.isEmpty())
489                         formatting.setTextFormatColourBackground(convertGdkColorToHtmlColour(backgroundColour));
490
491                 // multiline
492                 if (libglomFormatting.get_text_format_multiline()) {
493                         formatting.setTextFormatMultilineHeightLines(Utils.safeLongToInt(libglomFormatting
494                                         .get_text_format_multiline_height_lines()));
495                 }
496
497                 return formatting;
498         }
499
500         private LayoutItemField convertToGWTGlomLayoutItemField(LayoutItem_Field libglomLayoutItemField,
501                         boolean includeFormatting) {
502                 LayoutItemField layoutItemField = new LayoutItemField();
503
504                 // set type
505                 layoutItemField.setType(convertToGWTGlomFieldType(libglomLayoutItemField.get_glom_type()));
506
507                 // set title and name
508                 layoutItemField.setTitle(libglomLayoutItemField.get_title_or_name());
509                 layoutItemField.setName(libglomLayoutItemField.get_name());
510
511                 if (includeFormatting) {
512                         FieldFormatting glomFormatting = libglomLayoutItemField.get_formatting_used();
513                         Formatting formatting = convertFormatting(glomFormatting);
514
515                         // create a GlomNumericFormat DTO for numeric values
516                         if (libglomLayoutItemField.get_glom_type() == org.glom.libglom.Field.glom_field_type.TYPE_NUMERIC) {
517                                 formatting.setGlomNumericFormat(convertNumbericFormat(glomFormatting.getM_numeric_format()));
518                         }
519                         layoutItemField.setFormatting(formatting);
520                 }
521
522                 return layoutItemField;
523         }
524
525         /*
526          * This method converts a Field.glom_field_type to the equivalent ColumnInfo.FieldType. The need for this comes from
527          * the fact that the GWT FieldType classes can't be used with RPC and there's no easy way to use the java-libglom
528          * Field.glom_field_type enum with RPC. An enum identical to FieldFormatting.glom_field_type is included in the
529          * ColumnInfo class.
530          */
531         private LayoutItemField.GlomFieldType convertToGWTGlomFieldType(Field.glom_field_type type) {
532                 switch (type) {
533                 case TYPE_BOOLEAN:
534                         return LayoutItemField.GlomFieldType.TYPE_BOOLEAN;
535                 case TYPE_DATE:
536                         return LayoutItemField.GlomFieldType.TYPE_DATE;
537                 case TYPE_IMAGE:
538                         return LayoutItemField.GlomFieldType.TYPE_IMAGE;
539                 case TYPE_NUMERIC:
540                         return LayoutItemField.GlomFieldType.TYPE_NUMERIC;
541                 case TYPE_TEXT:
542                         return LayoutItemField.GlomFieldType.TYPE_TEXT;
543                 case TYPE_TIME:
544                         return LayoutItemField.GlomFieldType.TYPE_TIME;
545                 case TYPE_INVALID:
546                         Log.info("Returning TYPE_INVALID.");
547                         return LayoutItemField.GlomFieldType.TYPE_INVALID;
548                 default:
549                         Log.error("Recieved a type that I don't know about: " + Field.glom_field_type.class.getName() + "."
550                                         + type.toString() + ". Returning " + LayoutItemField.GlomFieldType.TYPE_INVALID.toString() + ".");
551                         return LayoutItemField.GlomFieldType.TYPE_INVALID;
552                 }
553         }
554
555         /*
556          * Converts a Gdk::Color (16-bits per channel) to an HTML colour (8-bits per channel) by discarding the least
557          * significant 8-bits in each channel.
558          */
559         private String convertGdkColorToHtmlColour(String gdkColor) {
560                 if (gdkColor.length() == 13)
561                         return gdkColor.substring(0, 3) + gdkColor.substring(5, 7) + gdkColor.substring(9, 11);
562                 else if (gdkColor.length() == 7) {
563                         // This shouldn't happen but let's deal with it if it does.
564                         Log.warn(documentID,
565                                         "Expected a 13 character string but received a 7 character string. Returning received string.");
566                         return gdkColor;
567                 } else {
568                         Log.error("Did not receive a 13 or 7 character string. Returning black HTML colour code.");
569                         return "#000000";
570                 }
571         }
572
573         /*
574          * This method converts a LayoutItem_Portal.navigation_type from java-libglom to the equivalent
575          * LayoutItemPortal.NavigationType from Online Glom. This conversion is required because the LayoutItem_Portal class
576          * from java-libglom can't be used with GWT-RPC. An enum identical to LayoutItem_Portal.navigation_type from
577          * java-libglom is included in the LayoutItemPortal data transfer object.
578          */
579         private LayoutItemPortal.NavigationType convertToGWTGlomNavigationType(
580                         LayoutItem_Portal.navigation_type navigationType) {
581                 switch (navigationType) {
582                 case NAVIGATION_NONE:
583                         return LayoutItemPortal.NavigationType.NAVIGATION_NONE;
584                 case NAVIGATION_AUTOMATIC:
585                         return LayoutItemPortal.NavigationType.NAVIGATION_AUTOMATIC;
586                 case NAVIGATION_SPECIFIC:
587                         return LayoutItemPortal.NavigationType.NAVIGATION_SPECIFIC;
588                 default:
589                         Log.error("Recieved an unknown NavigationType: " + LayoutItem_Portal.navigation_type.class.getName() + "."
590                                         + navigationType.toString() + ". Returning " + LayoutItemPortal.NavigationType.NAVIGATION_AUTOMATIC
591                                         + ".");
592                         return LayoutItemPortal.NavigationType.NAVIGATION_AUTOMATIC;
593                 }
594         }
595
596         /**
597          * Gets the table name to use when accessing the database and the document. This method guards against SQL injection
598          * attacks by returning the default table if the requested table is not in the database or if the table name has not
599          * been set.
600          * 
601          * @param tableName
602          *            The table name to validate.
603          * @return The table name to use.
604          */
605         private String getTableNameToUse(String tableName) {
606                 if (tableName == null || tableName.isEmpty() || !document.get_table_is_known(tableName)) {
607                         return document.get_default_table();
608                 }
609                 return tableName;
610         }
611
612 }