2 * Copyright (C) 2012 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.libglom;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Hashtable;
25 import java.util.List;
27 import javax.xml.parsers.DocumentBuilder;
28 import javax.xml.parsers.DocumentBuilderFactory;
29 import javax.xml.parsers.ParserConfigurationException;
31 import org.apache.commons.lang3.StringUtils;
32 import org.glom.web.shared.libglom.Field;
33 import org.glom.web.shared.libglom.NumericFormat;
34 import org.glom.web.shared.libglom.Relationship;
35 import org.glom.web.shared.libglom.Report;
36 import org.glom.web.shared.libglom.Translatable;
37 import org.glom.web.shared.libglom.layout.Formatting;
38 import org.glom.web.shared.libglom.layout.LayoutGroup;
39 import org.glom.web.shared.libglom.layout.LayoutItem;
40 import org.glom.web.shared.libglom.layout.LayoutItemField;
41 import org.glom.web.shared.libglom.layout.LayoutItemNotebook;
42 import org.glom.web.shared.libglom.layout.LayoutItemPortal;
43 import org.glom.web.shared.libglom.layout.LayoutItemPortal.NavigationType;
44 import org.glom.web.shared.libglom.layout.UsesRelationship;
45 import org.glom.web.shared.libglom.layout.UsesRelationshipImpl;
46 import org.glom.web.shared.libglom.layout.reportparts.LayoutItemGroupBy;
47 import org.jfree.util.Log;
48 import org.w3c.dom.Element;
49 import org.w3c.dom.Node;
50 import org.w3c.dom.NodeList;
51 import org.xml.sax.SAXException;
54 * @author Murray Cumming <murrayc@openismus.com>
57 public class Document {
59 @SuppressWarnings("serial")
60 private static class TableInfo extends Translatable {
61 public boolean isDefault;
62 public boolean isHidden;
64 private final Hashtable<String, Field> fieldsMap = new Hashtable<String, Field>();
65 private final Hashtable<String, Relationship> relationshipsMap = new Hashtable<String, Relationship>();
66 private final Hashtable<String, Report> reportsMap = new Hashtable<String, Report>();
68 private List<LayoutGroup> layoutGroupsList = new ArrayList<LayoutGroup>();
69 private List<LayoutGroup> layoutGroupsDetails = new ArrayList<LayoutGroup>();
72 private String fileURI = "";
73 private org.w3c.dom.Document xmlDocument = null;
75 private Translatable databaseTitle = new Translatable();
76 private List<String> translationAvailableLocales = new ArrayList<String>(); // TODO
77 private String connectionServer = "";
78 private String connectionDatabase = "";
79 private int connectionPort = 0;
80 private final Hashtable<String, TableInfo> tablesMap = new Hashtable<String, TableInfo>();
82 private static final String NODE_CONNECTION = "connection";
83 private static final String ATTRIBUTE_CONNECTION_SERVER = "server";
84 private static final String ATTRIBUTE_CONNECTION_DATABASE = "database";
85 private static final String ATTRIBUTE_CONNECTION_PORT = "port";
86 private static final String NODE_TABLE = "table";
87 private static final String ATTRIBUTE_NAME = "name";
88 private static final String ATTRIBUTE_TITLE = "title";
89 private static final String ATTRIBUTE_DEFAULT = "default";
90 private static final String ATTRIBUTE_HIDDEN = "hidden";
91 private static final String NODE_TRANSLATIONS_SET = "trans_set";
92 private static final String NODE_TRANSLATIONS = "trans";
93 private static final String ATTRIBUTE_TRANSLATION_LOCALE = "loc";
94 private static final String ATTRIBUTE_TRANSLATION_TITLE = "val";
95 private static final String NODE_REPORTS = "reports";
96 private static final String NODE_REPORT = "report";
97 private static final String NODE_FIELDS = "fields";
98 private static final String NODE_FIELD = "field";
99 private static final String ATTRIBUTE_PRIMARY_KEY = "primary_key";
100 private static final String ATTRIBUTE_FIELD_TYPE = "type";
101 private static final String NODE_FORMATTING = "formatting";
102 //private static final String ATTRIBUTE_TEXT_FORMAT_MULTILINE = "format_text_multiline";
103 private static final String ATTRIBUTE_USE_THOUSANDS_SEPARATOR = "format_thousands_separator";
104 private static final String ATTRIBUTE_DECIMAL_PLACES = "format_decimal_places";
105 private static final String NODE_RELATIONSHIPS = "relationships";
106 private static final String NODE_RELATIONSHIP = "relationship";
107 private static final String ATTRIBUTE_RELATIONSHIP_FROM_FIELD = "key";
108 private static final String ATTRIBUTE_RELATIONSHIP_TO_TABLE = "other_table";
109 private static final String ATTRIBUTE_RELATIONSHIP_TO_FIELD = "other_key";
110 private static final String NODE_DATA_LAYOUTS = "data_layouts";
111 private static final String NODE_DATA_LAYOUT = "data_layout";
112 private static final String NODE_DATA_LAYOUT_GROUPS = "data_layout_groups";
113 private static final String NODE_DATA_LAYOUT_GROUP = "data_layout_group";
114 private static final String ATTRIBUTE_LAYOUT_GROUP_COLUMNS_COUNT = "columns_count";
115 private static final String NODE_DATA_LAYOUT_NOTEBOOK = "data_layout_notebook";
116 private static final String NODE_DATA_LAYOUT_PORTAL = "data_layout_portal";
117 private static final String NODE_DATA_LAYOUT_PORTAL_NAVIGATIONRELATIONSHIP = "portal_navigation_relationship";
118 private static final String ATTRIBUTE_PORTAL_NAVIGATION_TYPE = "navigation_type";
119 private static final String ATTRIBUTE_PORTAL_NAVIGATION_TYPE_AUTOMATIC = "automatic";
120 private static final String ATTRIBUTE_PORTAL_NAVIGATION_TYPE_SPECIFIC = "specific";
121 private static final String ATTRIBUTE_PORTAL_NAVIGATION_TYPE_NONE = "none";
122 private static final String ATTRIBUTE_RELATIONSHIP_NAME = "relationship";
123 private static final String ATTRIBUTE_RELATED_RELATIONSHIP_NAME = "related_relationship";
124 private static final String NODE_DATA_LAYOUT_ITEM = "data_layout_item";
125 private static final String NODE_DATA_LAYOUT_ITEM_GROUPBY = "data_layout_item_groupby";
126 private static final String NODE_GROUPBY = "groupby";
127 private static final String NODE_SECONDARY_FIELDS = "secondary_fields";
128 private static final String ATTRIBUTE_USE_DEFAULT_FORMATTING = "use_default_formatting";
129 private static final String LAYOUT_NAME_DETAILS = "details";
130 private static final String LAYOUT_NAME_LIST = "list";
132 public void setFileURI(final String fileURI) {
133 this.fileURI = fileURI;
136 public String getFileURI() {
140 // TODO: Make sure these have the correct values.
141 public enum LoadFailureCodes {
142 LOAD_FAILURE_CODE_NONE, LOAD_FAILURE_CODE_NOT_FOUND, LOAD_FAILURE_CODE_FILE_VERSION_TOO_NEW
145 public boolean load() {
146 final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
147 DocumentBuilder documentBuilder;
149 documentBuilder = dbf.newDocumentBuilder();
150 } catch (final ParserConfigurationException e) {
151 // TODO Auto-generated catch block
157 xmlDocument = documentBuilder.parse(fileURI);
158 } catch (final SAXException e) {
159 // TODO Auto-generated catch block
162 } catch (final IOException e) {
163 // TODO Auto-generated catch block
168 final Element rootNode = xmlDocument.getDocumentElement();
169 if (rootNode.getNodeName() != "glom_document") {
170 Log.error("Unexpected XML root node name found: " + rootNode.getNodeName());
174 databaseTitle.setTitleOriginal(rootNode.getAttribute(ATTRIBUTE_TITLE));
176 // We first load the fields, relationships, etc,
178 final List<Node> listTableNodes = getChildrenByTagName(rootNode, NODE_TABLE);
179 for (final Node node : listTableNodes) {
180 if (!(node instanceof Element))
183 final Element element = (Element) node;
184 final TableInfo info = loadTableNodeBasic(element);
185 tablesMap.put(info.getName(), info);
188 // We then load the layouts for all tables, because they
189 // need the fields and relationships for all tables:
190 for (final Node node : listTableNodes) {
191 if (!(node instanceof Element))
194 final Element element = (Element) node;
195 final String tableName = element.getAttribute(ATTRIBUTE_NAME);
197 // We first load the fields, relationships, etc:
198 final TableInfo info = getTableInfo(tableName);
203 // We then load the layouts afterwards, because they
204 // need the fields and relationships:
205 loadTableLayouts(element, info);
207 tablesMap.put(info.getName(), info);
210 final Element nodeConnection = getElementByName(rootNode, NODE_CONNECTION);
211 if (nodeConnection != null) {
212 connectionServer = nodeConnection.getAttribute(ATTRIBUTE_CONNECTION_SERVER);
213 connectionDatabase = nodeConnection.getAttribute(ATTRIBUTE_CONNECTION_DATABASE);
214 connectionPort = getAttributeAsDecimal(nodeConnection,
215 nodeConnection.getAttribute(ATTRIBUTE_CONNECTION_PORT));
221 private Element getElementByName(final Element parentElement, final String tagName) {
222 final List<Node> listNodes = getChildrenByTagName(parentElement, tagName);
223 if (listNodes == null)
226 if (listNodes.size() == 0)
229 return (Element) listNodes.get(0);
232 private boolean getAttributeAsBoolean(final Element node, final String attributeName) {
233 final String str = node.getAttribute(attributeName);
237 return str.equals("true");
241 * @param elementFormatting
242 * @param aTTRIBUTE_DECIMAL_PLACES2
245 private int getAttributeAsDecimal(final Element node, final String attributeName) {
246 final String str = node.getAttribute(attributeName);
247 if (StringUtils.isEmpty(str))
250 return Integer.valueOf(str);
254 * Load a title and its translations.
257 * The XML Element that may contain a title attribute and a trans_set of translations of the title.
260 private void loadTitle(final Element node, final Translatable title) {
261 title.setName(node.getAttribute(ATTRIBUTE_NAME));
263 title.setTitleOriginal(node.getAttribute(ATTRIBUTE_TITLE));
265 final Element nodeSet = getElementByName(node, NODE_TRANSLATIONS_SET);
266 if (nodeSet == null) {
270 final List<Node> listNodes = getChildrenByTagName(nodeSet, NODE_TRANSLATIONS);
271 if (listNodes == null)
274 for (final Node transNode : listNodes) {
275 if (!(transNode instanceof Element)) {
279 final Element element = (Element) transNode;
281 final String locale = element.getAttribute(ATTRIBUTE_TRANSLATION_LOCALE);
282 final String translatedTitle = element.getAttribute(ATTRIBUTE_TRANSLATION_TITLE);
283 if (!StringUtils.isEmpty(locale) && !StringUtils.isEmpty(translatedTitle)) {
284 title.setTitle(translatedTitle, locale);
293 private TableInfo loadTableNodeBasic(final Element tableNode) {
294 final TableInfo info = new TableInfo();
295 loadTitle(tableNode, info);
296 final String tableName = info.getName();
298 info.isDefault = getAttributeAsBoolean(tableNode, ATTRIBUTE_DEFAULT);
299 info.isHidden = getAttributeAsBoolean(tableNode, ATTRIBUTE_HIDDEN);
301 // These should be loaded before the fields, because the fields use them.
302 final Element relationshipsNode = getElementByName(tableNode, NODE_RELATIONSHIPS);
303 if (relationshipsNode != null) {
304 final List<Node> listNodes = getChildrenByTagName(relationshipsNode, NODE_RELATIONSHIP);
305 for (final Node node : listNodes) {
306 if (!(node instanceof Element)) {
310 final Element element = (Element) node;
311 final Relationship relationship = new Relationship();
312 loadTitle(element, relationship);
313 relationship.setFromTable(tableName);
314 relationship.setFromField(element.getAttribute(ATTRIBUTE_RELATIONSHIP_FROM_FIELD));
315 relationship.setToTable(element.getAttribute(ATTRIBUTE_RELATIONSHIP_TO_TABLE));
316 relationship.setToField(element.getAttribute(ATTRIBUTE_RELATIONSHIP_TO_FIELD));
318 info.relationshipsMap.put(relationship.getName(), relationship);
322 final Element fieldsNode = getElementByName(tableNode, NODE_FIELDS);
323 if (fieldsNode != null) {
324 final List<Node> listNodes = getChildrenByTagName(fieldsNode, NODE_FIELD);
325 for (final Node node : listNodes) {
326 if (!(node instanceof Element)) {
330 final Element element = (Element) node;
331 final Field field = new Field();
332 loadField(element, field);
334 info.fieldsMap.put(field.getName(), field);
345 private void loadTableLayouts(final Element tableNode, final TableInfo info) {
346 final String tableName = info.getName();
348 final Element layoutsNode = getElementByName(tableNode, NODE_DATA_LAYOUTS);
349 if (layoutsNode != null) {
350 final List<Node> listNodes = getChildrenByTagName(layoutsNode, NODE_DATA_LAYOUT);
351 for (final Node node : listNodes) {
352 if (!(node instanceof Element)) {
356 final Element element = (Element) node;
357 final String name = element.getAttribute("name");
358 final List<LayoutGroup> listLayoutGroups = loadLayoutNode(element, tableName);
359 if (name.equals(LAYOUT_NAME_DETAILS)) {
360 info.layoutGroupsDetails = listLayoutGroups;
361 } else if (name.equals(LAYOUT_NAME_LIST)) {
362 info.layoutGroupsList = listLayoutGroups;
364 Log.error("loadTableNode(): unexpected layout name: " + name);
369 final Element reportsNode = getElementByName(tableNode, NODE_REPORTS);
370 if (reportsNode != null) {
371 final List<Node> listNodes = getChildrenByTagName(reportsNode, NODE_REPORT);
372 for (final Node node : listNodes) {
373 if (!(node instanceof Element)) {
377 final Element element = (Element) node;
378 final Report report = new Report();
379 loadReport(element, report, tableName);
381 info.reportsMap.put(report.getName(), report);
390 private List<LayoutGroup> loadLayoutNode(final Element node, final String tableName) {
395 final List<LayoutGroup> result = new ArrayList<LayoutGroup>();
397 final List<Node> listNodes = getChildrenByTagName(node, NODE_DATA_LAYOUT_GROUPS);
398 for (final Node nodeGroups : listNodes) {
399 if (!(nodeGroups instanceof Element)) {
403 final Element elementGroups = (Element) nodeGroups;
405 final NodeList list = elementGroups.getChildNodes();
406 final int num = list.getLength();
407 for (int i = 0; i < num; i++) {
408 final Node nodeLayoutGroup = list.item(i);
409 if (nodeLayoutGroup == null) {
413 if (!(nodeLayoutGroup instanceof Element)) {
417 final Element element = (Element) nodeLayoutGroup;
418 final String tagName = element.getTagName();
419 if (tagName == NODE_DATA_LAYOUT_GROUP) {
420 final LayoutGroup group = new LayoutGroup();
421 loadDataLayoutGroup(element, group, tableName);
423 } else if (tagName == NODE_DATA_LAYOUT_NOTEBOOK) {
424 final LayoutItemNotebook group = new LayoutItemNotebook();
425 loadDataLayoutGroup(element, group, tableName);
427 } else if (tagName == NODE_DATA_LAYOUT_PORTAL) {
428 final LayoutItemPortal portal = new LayoutItemPortal();
429 loadDataLayoutPortal(element, portal, tableName);
443 private void loadUsesRelationship(final Element element, final String tableName, final UsesRelationship item) {
444 if (element == null) {
452 final String relationshipName = element.getAttribute(ATTRIBUTE_RELATIONSHIP_NAME);
453 Relationship relationship = null;
454 if (!StringUtils.isEmpty(relationshipName)) {
455 // std::cout << " debug in : tableName=" << tableName << ", relationshipName=" << relationship_name <<
457 relationship = getRelationship(tableName, relationshipName);
458 item.setRelationship(relationship);
460 if (relationship == null) {
461 Log.error("relationship not found: " + relationshipName + ", in table: " + tableName);
465 final String relatedRelationshipName = element.getAttribute(ATTRIBUTE_RELATED_RELATIONSHIP_NAME);
466 if (!StringUtils.isEmpty(relatedRelationshipName) && (relationship != null)) {
467 final Relationship relatedRelationship = getRelationship(relationship.getToTable(), relatedRelationshipName);
468 if (relatedRelationship == null) {
469 Log.error("related relationship not found in table=" + relationship.getToTable() + ", name="
470 + relatedRelationshipName);
472 item.setRelatedRelationship(relatedRelationship);
478 * getElementsByTagName() is recursive, but we do not want that.
484 private List<Node> getChildrenByTagName(final Element parentNode, final String tagName) {
485 final List<Node> result = new ArrayList<Node>();
487 final NodeList list = parentNode.getElementsByTagName(tagName);
488 final int num = list.getLength();
489 for (int i = 0; i < num; i++) {
490 final Node node = list.item(i);
495 final Node itemParentNode = node.getParentNode();
496 if (itemParentNode.equals(parentNode)) {
508 private void loadDataLayoutGroup(final Element nodeGroup, final LayoutGroup group, final String tableName) {
509 loadTitle(nodeGroup, group);
511 group.setColumnCount( getAttributeAsDecimal(nodeGroup, ATTRIBUTE_LAYOUT_GROUP_COLUMNS_COUNT));
513 final NodeList listNodes = nodeGroup.getChildNodes();
514 final int num = listNodes.getLength();
515 for (int i = 0; i < num; i++) {
516 final Node node = listNodes.item(i);
517 if (!(node instanceof Element))
520 final Element element = (Element) node;
521 final String tagName = element.getTagName();
522 if (tagName == NODE_DATA_LAYOUT_GROUP) {
523 final LayoutGroup childGroup = new LayoutGroup();
524 loadDataLayoutGroup(element, childGroup, tableName);
525 group.addItem(childGroup);
526 } else if (tagName == NODE_DATA_LAYOUT_NOTEBOOK) {
527 final LayoutItemNotebook childGroup = new LayoutItemNotebook();
528 loadDataLayoutGroup(element, childGroup, tableName);
529 group.addItem(childGroup);
530 } else if (tagName == NODE_DATA_LAYOUT_PORTAL) {
531 final LayoutItemPortal childGroup = new LayoutItemPortal();
532 loadDataLayoutPortal(element, childGroup, tableName);
533 group.addItem(childGroup);
534 } else if (element.getTagName() == NODE_DATA_LAYOUT_ITEM) {
535 final LayoutItemField item = new LayoutItemField();
536 loadDataLayoutItemField(element, item, tableName);
538 } else if (element.getTagName() == NODE_DATA_LAYOUT_ITEM_GROUPBY) {
539 final LayoutItemGroupBy item = new LayoutItemGroupBy();
540 loadDataLayoutItemGroupBy(element, item, tableName);
551 private void loadDataLayoutItemGroupBy(final Element element, final LayoutItemGroupBy item, final String tableName) {
552 loadDataLayoutGroup(element, item, tableName);
554 final Element elementGroupBy = getElementByName(element, NODE_GROUPBY);
555 if (elementGroupBy == null) {
559 final LayoutItemField fieldGroupBy = new LayoutItemField();
560 loadDataLayoutItemField(elementGroupBy, fieldGroupBy, tableName);
561 item.setFieldGroupBy(fieldGroupBy);
563 final Element elementSecondaryFields = getElementByName(element, NODE_SECONDARY_FIELDS);
564 if (elementSecondaryFields == null) {
568 final Element elementLayoutGroup = getElementByName(elementSecondaryFields, NODE_DATA_LAYOUT_GROUP);
569 if (elementLayoutGroup != null) {
570 final LayoutGroup secondaryLayoutGroup = new LayoutGroup();
571 loadDataLayoutGroup(elementLayoutGroup, secondaryLayoutGroup, tableName);
572 item.setSecondaryFields(secondaryLayoutGroup);
580 private void loadDataLayoutItemField(final Element element, final LayoutItemField item, final String tableName) {
581 loadTitle(element, item);
582 loadUsesRelationship(element, tableName, item);
584 // Get the actual field:
585 final String fieldName = item.getName();
586 final String inTableName = item.getTableUsed(tableName);
587 final Field field = getField(inTableName, fieldName);
588 item.setFullFieldDetails(field);
590 item.setUseDefaultFormatting(getAttributeAsBoolean(element, ATTRIBUTE_USE_DEFAULT_FORMATTING));
592 final Element elementFormatting = getElementByName(element, NODE_FORMATTING);
593 if (elementFormatting != null) {
594 loadFormatting(elementFormatting, item.getFormatting());
602 private void loadDataLayoutPortal(final Element element, final LayoutItemPortal portal, final String tableName) {
603 loadUsesRelationship(element, tableName, portal);
604 final String relatedTableName = portal.getTableUsed(tableName);
605 loadDataLayoutGroup(element, portal, relatedTableName);
607 final Element elementNavigation = getElementByName(element, NODE_DATA_LAYOUT_PORTAL_NAVIGATIONRELATIONSHIP);
608 if (elementNavigation != null) {
609 final String navigationTypeAsString = elementNavigation.getAttribute(ATTRIBUTE_PORTAL_NAVIGATION_TYPE);
610 if (StringUtils.isEmpty(navigationTypeAsString)
611 || navigationTypeAsString == ATTRIBUTE_PORTAL_NAVIGATION_TYPE_AUTOMATIC) {
612 portal.setNavigationType(LayoutItemPortal.NavigationType.NAVIGATION_AUTOMATIC);
613 } else if (navigationTypeAsString == ATTRIBUTE_PORTAL_NAVIGATION_TYPE_NONE) {
614 portal.setNavigationType(LayoutItemPortal.NavigationType.NAVIGATION_NONE);
615 } else if (navigationTypeAsString == ATTRIBUTE_PORTAL_NAVIGATION_TYPE_SPECIFIC) {
616 // Read the specified relationship name:
617 final UsesRelationship relationshipNavigationSpecific = new UsesRelationshipImpl();
618 loadUsesRelationship(elementNavigation, relatedTableName, relationshipNavigationSpecific);
619 portal.setNavigationRelationshipSpecific(relationshipNavigationSpecific);
629 private void loadField(final Element element, final Field field) {
630 loadTitle(element, field);
632 Field.GlomFieldType fieldType = Field.GlomFieldType.TYPE_INVALID;
633 final String fieldTypeStr = element.getAttribute(ATTRIBUTE_FIELD_TYPE);
634 if (!StringUtils.isEmpty(fieldTypeStr)) {
635 if (fieldTypeStr.equals("Boolean")) {
636 fieldType = Field.GlomFieldType.TYPE_BOOLEAN;
637 } else if (fieldTypeStr.equals("Date")) {
638 fieldType = Field.GlomFieldType.TYPE_DATE;
639 } else if (fieldTypeStr.equals("Image")) {
640 fieldType = Field.GlomFieldType.TYPE_IMAGE;
641 } else if (fieldTypeStr.equals("Number")) {
642 fieldType = Field.GlomFieldType.TYPE_NUMERIC;
643 } else if (fieldTypeStr.equals("Text")) {
644 fieldType = Field.GlomFieldType.TYPE_TEXT;
645 } else if (fieldTypeStr.equals("Time")) {
646 fieldType = Field.GlomFieldType.TYPE_TIME;
650 field.setGlomFieldType(fieldType);
652 field.setPrimaryKey(getAttributeAsBoolean(element, ATTRIBUTE_PRIMARY_KEY));
653 loadTitle(element, field);
655 final Element elementFormatting = getElementByName(element, NODE_FORMATTING);
656 if (elementFormatting != null) {
657 loadFormatting(elementFormatting, field.getFormatting());
662 * @param elementFormatting
665 private void loadFormatting(final Element elementFormatting, final Formatting formatting) {
666 if (elementFormatting == null)
669 if (formatting == null)
672 // formatting.setTextFormatMultiline(getAttributeAsBoolean(elementFormatting, ATTRIBUTE_TEXT_FORMAT_MULTILINE));
674 final NumericFormat numericFormatting = formatting.getNumericFormat();
675 if (numericFormatting != null) {
676 numericFormatting.setUseThousandsSeparator(getAttributeAsBoolean(elementFormatting,
677 ATTRIBUTE_USE_THOUSANDS_SEPARATOR));
678 numericFormatting.setDecimalPlaces(getAttributeAsDecimal(elementFormatting, ATTRIBUTE_DECIMAL_PLACES));
687 private void loadReport(final Element element, final Report report, final String tableName) {
688 report.setName(element.getAttribute(ATTRIBUTE_NAME));
689 loadTitle(element, report);
691 final List<LayoutGroup> listLayoutGroups = loadLayoutNode(element, tableName);
693 // A report can actually only have one LayoutGroup,
694 // though it uses the same XML structure as List and Details layouts,
695 // which (wrongly) suggests that it can have more than one group.
696 LayoutGroup layoutGroup = null;
697 if (!listLayoutGroups.isEmpty()) {
698 layoutGroup = listLayoutGroups.get(0);
701 report.setLayoutGroup(layoutGroup);
704 private TableInfo getTableInfo(final String tableName) {
705 return tablesMap.get(tableName);
708 public enum HostingMode {
709 HOSTING_MODE_POSTGRES_CENTRAL, HOSTING_MODE_POSTGRES_SELF, HOSTING_MODE_SQLITE
712 public String getDatabaseTitle(final String locale) {
713 return databaseTitle.getTitle(locale);
716 public String getDatabaseTitleOriginal() {
717 return databaseTitle.getTitleOriginal();
720 public List<String> getTranslationAvailableLocales() {
721 return translationAvailableLocales;
724 public Document.HostingMode getHostingMode() {
725 return HostingMode.HOSTING_MODE_POSTGRES_CENTRAL; // TODO
728 public String getConnectionServer() {
729 return connectionServer;
732 public long getConnectionPort() {
733 return connectionPort;
736 public String getConnectionDatabase() {
737 return connectionDatabase;
740 public List<String> getTableNames() {
741 // TODO: Return a Set?
742 return new ArrayList<String>(tablesMap.keySet());
745 public boolean getTableIsHidden(final String tableName) {
746 final TableInfo info = getTableInfo(tableName);
751 return info.isHidden;
754 public String getTableTitle(final String tableName, final String locale) {
755 final TableInfo info = getTableInfo(tableName);
760 return info.getTitle(locale);
763 public String getDefaultTable() {
764 for (final TableInfo info : tablesMap.values()) {
765 if (info.isDefault) {
766 return info.getName();
773 public boolean getTableIsKnown(final String tableName) {
774 final TableInfo info = getTableInfo(tableName);
782 public List<Field> getTableFields(final String tableName) {
783 final TableInfo info = getTableInfo(tableName);
787 return new ArrayList<Field>(info.fieldsMap.values());
790 public Field getField(final String tableName, final String strFieldName) {
791 final TableInfo info = getTableInfo(tableName);
795 return info.fieldsMap.get(strFieldName);
798 public List<LayoutGroup> getDataLayoutGroups(final String layoutName, final String parentTableName) {
799 final TableInfo info = getTableInfo(parentTableName);
801 return new ArrayList<LayoutGroup>();
803 if (layoutName == LAYOUT_NAME_DETAILS) {
804 return info.layoutGroupsDetails;
805 } else if (layoutName == LAYOUT_NAME_LIST) {
806 return info.layoutGroupsList;
808 return new ArrayList<LayoutGroup>();
812 public List<String> getReportNames(final String tableName) {
813 final TableInfo info = getTableInfo(tableName);
815 return new ArrayList<String>();
817 return new ArrayList<String>(info.reportsMap.keySet());
820 public Report getReport(final String tableName, final String reportName) {
821 final TableInfo info = getTableInfo(tableName);
825 return info.reportsMap.get(reportName);
833 public Relationship getFieldUsedInRelationshipToOne(final String tableName, final LayoutItemField layoutField) {
835 if (layoutField == null) {
836 Log.error("layoutField was null");
840 Relationship result = null;
842 final String tableUsed = layoutField.getTableUsed(tableName);
843 final TableInfo info = getTableInfo(tableUsed);
845 // This table is special. We would not create a relationship to it using a field:
846 // if(tableUsed == GLOM_STANDARD_TABLE_PREFS_TABLE_NAME)
849 Log.error("table not found: " + tableUsed);
853 // Look at each relationship:
854 final String fieldName = layoutField.getName();
855 for (final Relationship relationship : info.relationshipsMap.values()) {
856 if (relationship != null) {
857 // If the relationship uses the field
858 if (relationship.getFromField() == fieldName) {
859 // if the to_table is not hidden:
860 if (!getTableIsHidden(relationship.getToTable())) {
861 // TODO_Performance: The use of this convenience method means we get the full relationship
862 // information again:
863 if (getRelationshipIsToOne(tableName, relationship.getName())) {
864 result = relationship;
876 * @param relationshipName
879 private boolean getRelationshipIsToOne(final String tableName, final String relationshipName) {
880 final Relationship relationship = getRelationship(tableName, relationshipName);
881 if (relationship != null) {
882 final Field fieldTo = getField(relationship.getToTable(), relationship.getToField());
883 if (fieldTo != null) {
884 return (fieldTo.getPrimaryKey() || fieldTo.getUniqueKey());
893 * @param relationshipName
896 private Relationship getRelationship(final String tableName, final String relationshipName) {
897 final TableInfo info = getTableInfo(tableName);
899 Log.error("table not found: " + tableName);
903 return info.relationshipsMap.get(relationshipName);
906 public class TableToViewDetails {
907 public String tableName;
908 public UsesRelationship usesRelationship;
914 * @param relationship
918 public TableToViewDetails getPortalSuitableTableToViewDetails(LayoutItemPortal portal) {
919 UsesRelationship navigationRelationship = null;
921 // Check whether a relationship was specified:
922 if (portal.getNavigationType() == NavigationType.NAVIGATION_AUTOMATIC) {
923 navigationRelationship = getPortalNavigationRelationshipAutomatic(portal);
925 navigationRelationship = portal.getNavigationRelationshipSpecific();
928 // Get the navigation table name from the chosen relationship:
929 String directlyRelatedTableName = portal.getTableUsed("" /* not relevant */);
931 // The navigation_table_name (and therefore, the table_name output parameter,
932 // as well) stays empty if the navrel type was set to none.
933 String navigationTableName = null;
934 if (navigationRelationship != null) {
935 navigationTableName = navigationRelationship.getTableUsed(directlyRelatedTableName);
936 } else if (portal.getNavigationType() != NavigationType.NAVIGATION_NONE) {
937 // An empty result from get_portal_navigation_relationship_automatic() or
938 // get_navigation_relationship_specific() means we should use the directly related table:
939 navigationTableName = directlyRelatedTableName;
942 if (StringUtils.isEmpty(navigationTableName)) {
947 Log.error("document is null.");
951 if (getTableIsHidden(navigationTableName)) {
952 Log.error("navigation_table_name indicates a hidden table: " + navigationTableName);
956 TableToViewDetails result = new TableToViewDetails();
957 result.tableName = navigationTableName;
958 result.usesRelationship = navigationRelationship;
967 private UsesRelationship getPortalNavigationRelationshipAutomatic(LayoutItemPortal portal) {
972 // If the related table is not hidden then we can just navigate to that:
973 final String direct_related_table_name = portal.getTableUsed("" /* parent table - not relevant */);
974 if (!getTableIsHidden(direct_related_table_name)) {
975 // Non-hidden tables can just be shown directly. Navigate to it:
978 // If the related table is hidden,
979 // then find a suitable related non-hidden table by finding the first layout field that mentions one:
980 final LayoutItemField field = getPortalFieldIsFromNonHiddenRelatedRecord(portal);
982 return field; // Returns the UsesRelationship base part. (A relationship belonging to the portal's
985 // Instead, find a key field that's used in a relationship,
986 // and pretend that we are showing the to field as a related field:
987 final Relationship fieldIndentifies = getPortalFieldIdentifiesNonHiddenRelatedRecord(portal);
988 if (fieldIndentifies != null) {
989 UsesRelationship result = new UsesRelationshipImpl();
990 result.setRelationship(fieldIndentifies);
996 // There was no suitable related table to show:
1005 private LayoutItemField getPortalFieldIsFromNonHiddenRelatedRecord(LayoutItemPortal portal) {
1006 // Find the first field that is from a non-hidden related table.
1012 LayoutItemField result = null;
1014 final String parent_table_name = portal.getTableUsed("" /* parent table - not relevant */);
1016 final List<LayoutItem> items = portal.getItems();
1017 for (LayoutItem item : items) {
1018 if (item instanceof LayoutItemField) {
1019 LayoutItemField field = (LayoutItemField) item;
1020 if (field.getHasRelationshipName()) {
1021 final String table_name = field.getTableUsed(parent_table_name);
1022 if (!(getTableIsHidden(table_name)))
1032 * @param used_in_relationship
1037 private Relationship getPortalFieldIdentifiesNonHiddenRelatedRecord(LayoutItemPortal portal) {
1038 // Find the first field that is from a non-hidden related table.
1041 Log.error("document is null");
1045 final String parent_table_name = portal.getTableUsed("" /* parent table - not relevant */);
1047 List<LayoutItem> items = portal.getItems();
1048 for (LayoutItem item : items) {
1049 if (item instanceof LayoutItemField) {
1050 LayoutItemField field = (LayoutItemField) item;
1051 if (field.getHasRelationshipName()) {
1052 final Relationship relationship = getFieldUsedInRelationshipToOne(parent_table_name, field);
1053 if (relationship != null) {
1054 final String table_name = relationship.getToTable();
1055 if (!StringUtils.isEmpty(table_name)) {
1056 if (!(getTableIsHidden(table_name))) {
1057 return relationship;