• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

knowledgepixels / nanodash / 17852532121

19 Sep 2025 08:10AM UTC coverage: 13.568% (-0.3%) from 13.87%
17852532121

push

github

tkuhn
feat: Switch to QueryRef provided by nanopub, using multimap

428 of 4008 branches covered (10.68%)

Branch coverage included in aggregate %.

1104 of 7283 relevant lines covered (15.16%)

0.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
src/main/java/com/knowledgepixels/nanodash/component/ExploreDataTable.java
1
package com.knowledgepixels.nanodash.component;
2

3
import java.io.Serializable;
4
import java.util.ArrayList;
5
import java.util.Comparator;
6
import java.util.Iterator;
7
import java.util.List;
8

9
import org.apache.wicket.Component;
10
import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxNavigationToolbar;
11
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
12
import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortState;
13
import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
14
import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
15
import org.apache.wicket.extensions.markup.html.repeater.data.table.HeadersToolbar;
16
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
17
import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
18
import org.apache.wicket.extensions.markup.html.repeater.data.table.NoRecordsToolbar;
19
import org.apache.wicket.extensions.markup.html.repeater.util.SingleSortState;
20
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
21
import org.apache.wicket.markup.html.basic.Label;
22
import org.apache.wicket.markup.html.panel.Panel;
23
import org.apache.wicket.markup.repeater.Item;
24
import org.apache.wicket.model.IModel;
25
import org.apache.wicket.model.Model;
26
import org.nanopub.Nanopub;
27
import org.nanopub.extra.services.ApiResponse;
28
import org.nanopub.extra.services.ApiResponseEntry;
29
import org.nanopub.extra.services.QueryRef;
30
import org.slf4j.Logger;
31
import org.slf4j.LoggerFactory;
32

33
import com.knowledgepixels.nanodash.ApiCache;
34
import com.knowledgepixels.nanodash.User;
35
import com.knowledgepixels.nanodash.Utils;
36

37
/**
38
 * A component that displays a data table of nanopublication references.
39
 */
40
public class ExploreDataTable extends Panel {
41

42
    private static final String refQueryName = "find-uri-references";
43
    private static final Logger logger = LoggerFactory.getLogger(ExploreDataTable.class);
×
44

45
    private ExploreDataTable(String id, String ref, ApiResponse response) {
46
        super(id);
×
47
        setOutputMarkupId(true);
×
48

49
        List<IColumn<ApiResponseEntry, String>> columns = new ArrayList<>();
×
50
        DataProvider dp;
51
        try {
52
            columns.add(new Column("Nanopublication", "np", ref));
×
53
            columns.add(new Column("Part", "graphpred", ref));
×
54
            columns.add(new Column("Subject", "subj", ref));
×
55
            columns.add(new Column("Predicate", "pred", ref));
×
56
            columns.add(new Column("Object", "obj", ref));
×
57
            columns.add(new Column("Published By", "pubkey", ref));
×
58
            columns.add(new Column("Published On", "date", ref));
×
59
            dp = new DataProvider(filterData(response.getData(), ref));
×
60
            DataTable<ApiResponseEntry, String> table = new DataTable<>("datatable", columns, dp, 10);
×
61
            table.addBottomToolbar(new AjaxNavigationToolbar(table));
×
62
            table.addBottomToolbar(new NoRecordsToolbar(table));
×
63
            table.addTopToolbar(new HeadersToolbar<String>(table, dp));
×
64
            table.setOutputMarkupId(true);
×
65
            add(table);
×
66
            add(new Label("message", "").setVisible(false));
×
67
        } catch (Exception ex) {
×
68
            logger.error("Could not create data table for reference: {}", ref, ex);
×
69
            add(new Label("datatable", "").setVisible(false));
×
70
            add(new Label("message", "Could not load data table."));
×
71
            add(new Label("show-all").setVisible(false));
×
72
        }
×
73
    }
×
74

75

76
    private List<ApiResponseEntry> filterData(List<ApiResponseEntry> data, String nanopubUri) {
77
        List<ApiResponseEntry> filteredList = new ArrayList<>();
×
78
        Nanopub np = Utils.getAsNanopub(nanopubUri);
×
79
        if (np == null) return data;
×
80
        for (ApiResponseEntry e : data) {
×
81
            if (np == null || !nanopubUri.equals(e.get("np"))) {
×
82
                filteredList.add(e);
×
83
            }
84
        }
×
85
        return filteredList;
×
86
    }
87

88

89
    private class Column extends AbstractColumn<ApiResponseEntry, String> {
90

91
        private String key, current;
92

93
        /**
94
         * Constructor for a column in the data table.
95
         *
96
         * @param title   The title of the column.
97
         * @param key     The key used to retrieve data from ApiResponseEntry.
98
         * @param current The current value to highlight in the column.
99
         */
100
        public Column(String title, String key, String current) {
×
101
            super(new Model<String>(title), key);
×
102
            this.key = key;
×
103
            this.current = current;
×
104
        }
×
105

106
        @Override
107
        public void populateItem(Item<ICellPopulator<ApiResponseEntry>> cellItem, String componentId, IModel<ApiResponseEntry> rowModel) {
108
            String value = rowModel.getObject().get(key);
×
109
            if (value.equals(current)) {
×
110
                cellItem.add(new Label(componentId, "<strong>" + IriItem.getShortNameFromURI(value) + "</strong>").setEscapeModelStrings(false));
×
111
            } else if (value.matches("https?://.+")) {
×
112
                cellItem.add(new NanodashLink(componentId, value));
×
113
            } else {
114
                if (key.equals("pubkey")) {
×
115
                    cellItem.add(new Label(componentId, User.getShortDisplayNameForPubkeyhash(null, Utils.createSha256HexHash(value))));
×
116
                } else {
117
                    cellItem.add(new Label(componentId, value));
×
118
                }
119
            }
120
        }
×
121

122
    }
123

124

125
    private class DataProvider implements ISortableDataProvider<ApiResponseEntry, String> {
126

127
        private List<ApiResponseEntry> data = new ArrayList<>();
×
128
        private SingleSortState<String> sortState = new SingleSortState<>();
×
129

130
        /**
131
         * Default constructor that initializes the sort state.
132
         */
133
        public DataProvider() {
×
134
            sortState.setSort(new SortParam<String>("date", false));
×
135
        }
×
136

137
        /**
138
         * Constructor that initializes the data provider with a list of ApiResponseEntry.
139
         *
140
         * @param data The list of ApiResponseEntry to be used in the data provider.
141
         */
142
        public DataProvider(List<ApiResponseEntry> data) {
143
            this();
×
144
            this.data = data;
×
145
        }
×
146

147
        @Override
148
        public Iterator<? extends ApiResponseEntry> iterator(long first, long count) {
149
            List<ApiResponseEntry> copy = new ArrayList<>(data);
×
150
            ApiResponseComparator comparator = new ApiResponseComparator(sortState.getSort());
×
151
            copy.sort(comparator);
×
152
            return Utils.subList(copy, first, first + count).iterator();
×
153
        }
154

155
        @Override
156
        public IModel<ApiResponseEntry> model(ApiResponseEntry object) {
157
            return new Model<ApiResponseEntry>(object);
×
158
        }
159

160
        @Override
161
        public long size() {
162
            return data.size();
×
163
        }
164

165
        @Override
166
        public ISortState<String> getSortState() {
167
            return sortState;
×
168
        }
169

170
        @Override
171
        public void detach() {
172
        }
×
173

174
    }
175

176
    private class ApiResponseComparator implements Comparator<ApiResponseEntry>, Serializable {
177

178
        private SortParam<String> sortParam;
179

180
        /**
181
         * Constructor that initializes the comparator with a sort parameter.
182
         *
183
         * @param sortParam The sort parameter defining the property and order for comparison.
184
         */
185
        public ApiResponseComparator(SortParam<String> sortParam) {
×
186
            this.sortParam = sortParam;
×
187
        }
×
188

189
        @Override
190
        public int compare(ApiResponseEntry o1, ApiResponseEntry o2) {
191
            String p = sortParam.getProperty();
×
192
            int result = o1.get(p).compareTo(o2.get(p));
×
193
            if (!sortParam.isAscending()) result = -result;
×
194
            return result;
×
195
        }
196

197
    }
198

199
    /**
200
     * Creates a new ExploreDataTable component.
201
     *
202
     * @param markupId the Wicket markup ID for the component
203
     * @param ref      the reference URI to be displayed in the table
204
     * @return a new ExploreDataTable component or an ApiResultComponent if the data is not cached
205
     */
206
    public static Component createComponent(final String markupId, final String ref) {
207
        QueryRef queryRef = new QueryRef(refQueryName, "ref", ref);
×
208
        ApiResponse response = ApiCache.retrieveResponse(queryRef);
×
209
        if (response != null) {
×
210
            return new ExploreDataTable(markupId, ref, response);
×
211
        } else {
212
            return new ApiResultComponent(markupId, queryRef) {
×
213

214
                @Override
215
                public Component getApiResultComponent(String markupId, ApiResponse response) {
216
                    return new ExploreDataTable(markupId, ref, response);
×
217
                }
218

219
            };
220
        }
221
    }   
222

223
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc