diff --git a/Model/lib/rng/wdkModel.rng b/Model/lib/rng/wdkModel.rng index a1524fa48..b76975532 100644 --- a/Model/lib/rng/wdkModel.rng +++ b/Model/lib/rng/wdkModel.rng @@ -1685,6 +1685,10 @@ + + + + @@ -1709,6 +1713,10 @@ + + + + diff --git a/Model/src/main/java/org/gusdb/wdk/core/api/JsonKeys.java b/Model/src/main/java/org/gusdb/wdk/core/api/JsonKeys.java index 667b21465..abcaba552 100644 --- a/Model/src/main/java/org/gusdb/wdk/core/api/JsonKeys.java +++ b/Model/src/main/java/org/gusdb/wdk/core/api/JsonKeys.java @@ -61,6 +61,7 @@ public class JsonKeys { public static final String HELP = "help"; public static final String HTML_HELP = "htmlHelp"; public static final String SUGGEST_TEXT = "suggestText"; + public static final String TOOLTIP = "tooltip"; public static final String DESCRIPTION = "description"; public static final String SHORT_DESCRIPTION = "shortDescription"; public static final String SUMMARY = "summary"; diff --git a/Model/src/main/java/org/gusdb/wdk/model/ModelXmlParser.java b/Model/src/main/java/org/gusdb/wdk/model/ModelXmlParser.java index 2a809961c..34ea8a791 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/ModelXmlParser.java +++ b/Model/src/main/java/org/gusdb/wdk/model/ModelXmlParser.java @@ -1038,6 +1038,8 @@ private static void configureAttributeFields(Digester digester) { digester.addCallMethod("*/linkAttribute/url", "setText", 0); configureNode(digester, "*/linkAttribute/displayText", WdkModelText.class, "addDisplayText"); digester.addCallMethod("*/linkAttribute/displayText", "setText", 0); + configureNode(digester, "*/linkAttribute/tooltip", WdkModelText.class, "addTooltip"); + digester.addCallMethod("*/linkAttribute/tooltip", "setText", 0); configureAttributeReporters(digester, "linkAttribute"); // text attribute @@ -1047,6 +1049,8 @@ private static void configureAttributeFields(Digester digester) { digester.addCallMethod("*/textAttribute/text", "setText", 0); configureNode(digester, "*/textAttribute/display", WdkModelText.class, "addDisplay"); digester.addCallMethod("*/textAttribute/display", "setText", 0); + configureNode(digester, "*/linkAttribute/tooltip", WdkModelText.class, "addTooltip"); + digester.addCallMethod("*/linkAttribute/tooltip", "setText", 0); configureAttributeReporters(digester, "textAttribute"); } diff --git a/Model/src/main/java/org/gusdb/wdk/model/record/attribute/LinkAttributeField.java b/Model/src/main/java/org/gusdb/wdk/model/record/attribute/LinkAttributeField.java index 69d06f556..57408482c 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/record/attribute/LinkAttributeField.java +++ b/Model/src/main/java/org/gusdb/wdk/model/record/attribute/LinkAttributeField.java @@ -33,12 +33,14 @@ public class LinkAttributeField extends DerivedAttributeField { private static final String DEFAULT_TYPE = "link"; // fields set by XML parsing - private List _urls = new ArrayList(); - private List _displayTexts = new ArrayList(); + private List _urls = new ArrayList<>(); + private List _displayTexts = new ArrayList<>(); + private List _tooltips = new ArrayList<>(); // resolved fields private String _url; private String _displayText; + private String _tooltip; private boolean _newWindow = false; @@ -78,11 +80,20 @@ public String getDisplayText() { return _displayText; } + public void addTooltip(WdkModelText tooltip) { + _tooltips.add(tooltip); + } + + public String getTooltip() { + return _tooltip == null ? "" : _tooltip; + } + @Override public void excludeResources(String projectId) throws WdkModelException { super.excludeResources(projectId); _url = excludeModelText(_urls, projectId, "url", true); _displayText = excludeModelText(_displayTexts, projectId, "displayText", true); + _tooltip = excludeModelText(_tooltips, projectId, "tooltip", false); } @Override @@ -90,6 +101,7 @@ protected Collection getDependencies() throws WdkModelException Map dependents = new LinkedHashMap<>(); if (_displayText != null) dependents.putAll(parseFields(_displayText)); if (_url != null) dependents.putAll(parseFields(_url)); + if (_tooltip != null) dependents.putAll(parseFields(_tooltip)); return dependents.values(); } } diff --git a/Model/src/main/java/org/gusdb/wdk/model/record/attribute/LinkAttributeValue.java b/Model/src/main/java/org/gusdb/wdk/model/record/attribute/LinkAttributeValue.java index 8c2fd5b48..f50d968c4 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/record/attribute/LinkAttributeValue.java +++ b/Model/src/main/java/org/gusdb/wdk/model/record/attribute/LinkAttributeValue.java @@ -19,6 +19,7 @@ public class LinkAttributeValue extends DerivedAttributeValue { private String _displayText; private String _url; + private String _tooltip; public LinkAttributeValue(LinkAttributeField field, AttributeValueContainer container) { super(field, container); @@ -38,6 +39,13 @@ public String getUrl() throws WdkModelException, WdkUserException { return _url; } + public String getTooltip() throws WdkModelException, WdkUserException { + if (_tooltip == null) { + _tooltip = populateMacros(((TextAttributeField)_field).getTooltip()); + } + return _tooltip; + } + /** * Get the text representation of the url (e.g. for reports). */ diff --git a/Model/src/main/java/org/gusdb/wdk/model/record/attribute/TextAttributeField.java b/Model/src/main/java/org/gusdb/wdk/model/record/attribute/TextAttributeField.java index e59f312dd..d676e682d 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/record/attribute/TextAttributeField.java +++ b/Model/src/main/java/org/gusdb/wdk/model/record/attribute/TextAttributeField.java @@ -22,14 +22,17 @@ public class TextAttributeField extends DerivedAttributeField { // fields set by XML parsing private final List _texts; private final List _displays; + private final List _tooltips; // resolved fields private String _text; private String _display; + private String _tooltip; public TextAttributeField() { _texts = new ArrayList<>(); _displays = new ArrayList<>(); + _tooltips = new ArrayList<>(); _dataType = AttributeFieldDataType.STRING; } @@ -49,11 +52,20 @@ public String getDisplay() { return (_display != null) ? _display : _text; } + public void addTooltip(WdkModelText tooltip) { + _tooltips.add(tooltip); + } + + public String getTooltip() { + return _tooltip == null ? "" : _tooltip; + } + @Override public void excludeResources(String projectId) throws WdkModelException { super.excludeResources(projectId); _text = excludeModelText(_texts, projectId, "text", true); _display = excludeModelText(_displays, projectId, "display", false); + _tooltip = excludeModelText(_tooltips, projectId, "tooltip", false); } @Override @@ -62,6 +74,7 @@ public Collection getDependencies() throws WdkModelException { Map dependents = new LinkedHashMap<>(); if (_display!= null) dependents.putAll(parseFields(_display)); if (_text != null) dependents.putAll(parseFields(_text)); + if (_tooltip != null) dependents.putAll(parseFields(_tooltip)); return dependents.values(); } } diff --git a/Model/src/main/java/org/gusdb/wdk/model/record/attribute/TextAttributeValue.java b/Model/src/main/java/org/gusdb/wdk/model/record/attribute/TextAttributeValue.java index 7a6fe10eb..320538a51 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/record/attribute/TextAttributeValue.java +++ b/Model/src/main/java/org/gusdb/wdk/model/record/attribute/TextAttributeValue.java @@ -25,6 +25,11 @@ public class TextAttributeValue extends DerivedAttributeValue { */ private String _display; + /** + * Optional value that can be used to display extra information about this data value in a UI. + */ + private String _tooltip; + /** * @param field * @param container @@ -49,4 +54,10 @@ public String getDisplay() throws WdkModelException, WdkUserException { return _display; } + public String getTooltip() throws WdkModelException, WdkUserException { + if (_tooltip == null) { + _tooltip = populateMacros(((TextAttributeField)_field).getTooltip()); + } + return _tooltip; + } } diff --git a/Model/src/main/java/org/gusdb/wdk/model/report/config/AnswerDetails.java b/Model/src/main/java/org/gusdb/wdk/model/report/config/AnswerDetails.java index 8b4711323..bfc6c14cd 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/report/config/AnswerDetails.java +++ b/Model/src/main/java/org/gusdb/wdk/model/report/config/AnswerDetails.java @@ -15,7 +15,7 @@ public class AnswerDetails { public static final Integer ALL_RECORDS = -1; public enum AttributeFormat { - TEXT, DISPLAY; + TEXT, DISPLAY, EXPANDED; } // use factory method to construct from JSON diff --git a/Model/src/main/java/org/gusdb/wdk/model/report/config/AnswerDetailsFactory.java b/Model/src/main/java/org/gusdb/wdk/model/report/config/AnswerDetailsFactory.java index b28c362d9..f72227ee3 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/report/config/AnswerDetailsFactory.java +++ b/Model/src/main/java/org/gusdb/wdk/model/report/config/AnswerDetailsFactory.java @@ -81,7 +81,7 @@ public static AnswerDetails createDefault(Question question) { * tables: [ tableName: String ], or special string, * sorting: [ { attributeName: String, direction: Enum[ASC,DESC] } ], * contentDisposition: 'inline' (default) OR 'attachment' - * attributeFormat: 'display' (default) OR 'text' + * attributeFormat: 'display' (default) OR 'text' OR 'expanded' * } * * All values are optional. diff --git a/Model/src/main/java/org/gusdb/wdk/model/report/util/RecordFormatter.java b/Model/src/main/java/org/gusdb/wdk/model/report/util/RecordFormatter.java index 2c0dce59b..5165078ce 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/report/util/RecordFormatter.java +++ b/Model/src/main/java/org/gusdb/wdk/model/report/util/RecordFormatter.java @@ -109,33 +109,40 @@ public static JSONArray getTableRowsJson(RecordInstance record, String tableName private static Object getAttributeValueJson(AttributeValue attr, AttributeFormat attributeFormat) throws WdkModelException, WdkUserException { - if (attributeFormat == AttributeFormat.DISPLAY) { - // for display format, show - if (attr instanceof LinkAttributeValue) { - LinkAttributeValue linkAttr = (LinkAttributeValue)attr; - String displayText = linkAttr.getDisplayText(); - - // Treat an empty displayText as null - if (displayText == null || displayText.isEmpty()) { - return JSONObject.NULL; - } - - return new JSONObject() - .put(JsonKeys.URL, linkAttr.getUrl()) - .put(JsonKeys.DISPLAY_TEXT, displayText); - } - - if (attr instanceof TextAttributeValue){ - return attr.getDisplay(); + if (attr instanceof LinkAttributeValue ) { + LinkAttributeValue linkAttr = (LinkAttributeValue)attr; + switch(attributeFormat) { + case TEXT: + return linkAttr.getUrl(); + case DISPLAY: + case EXPANDED: + // return JSON object with each of the values for proper display in a UI + return new JSONObject() + .put(JsonKeys.URL, linkAttr.getUrl()) + .put(JsonKeys.DISPLAY_TEXT, linkAttr.getDisplayText()) + .put(JsonKeys.TOOLTIP, nullIfEmpty(linkAttr.getTooltip())); } } - else { - if (attr instanceof LinkAttributeValue) { - return ((LinkAttributeValue)attr).getUrl(); + + if (attr instanceof TextAttributeValue) { + TextAttributeValue textAttr = (TextAttributeValue)attr; + switch(attributeFormat) { + case TEXT: return nullIfEmpty(textAttr.getValue()); + case DISPLAY: return textAttr.getDisplay(); + case EXPANDED: + // return JSON object with each of the values for proper display in a UI + return new JSONObject() + .put(JsonKeys.VALUE, textAttr.getValue()) + .put(JsonKeys.DISPLAY, nullIfEmpty(textAttr.getDisplay())) + .put(JsonKeys.TOOLTIP, nullIfEmpty(textAttr.getTooltip())); } } + // outside of above cases, return value of attribute or JSON null if empty - String value = attr.getValue(); - return value == null ? JSONObject.NULL : value; + return nullIfEmpty(attr.getValue()); + } + + private static Object nullIfEmpty(String str) { + return str == null || str.isBlank() ? JSONObject.NULL : str; } }