1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.metis.viewer;
18
19 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20 import io.github.tonywasher.joceanus.oceanus.decimal.OceanusDecimal;
21 import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
22 import io.github.tonywasher.joceanus.oceanus.profile.OceanusProfile;
23 import io.github.tonywasher.joceanus.metis.data.MetisDataDelta;
24 import io.github.tonywasher.joceanus.metis.data.MetisDataFieldValue;
25 import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataList;
26 import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataMap;
27 import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
28 import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldDef;
29 import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldSetDef;
30 import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldVersionedDef;
31 import io.github.tonywasher.joceanus.metis.field.MetisFieldValidation;
32 import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionHistory;
33 import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionedItem;
34
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Map.Entry;
39
40
41
42
43 public class MetisViewerFormatter {
44
45
46
47 protected static final int ITEMS_PER_PAGE = 50;
48
49
50
51
52 private static final String COLUMN_INDEX = "Index";
53
54
55
56
57 private static final String COLUMN_FIELD = MetisViewerResource.VIEWER_COLUMN_FIELD.getValue();
58
59
60
61
62 private static final String COLUMN_KEY = MetisViewerResource.VIEWER_COLUMN_KEY.getValue();
63
64
65
66
67 private static final String COLUMN_VALUE = MetisViewerResource.VIEWER_COLUMN_VALUE.getValue();
68
69
70
71
72 private static final String TABLE_LIST = "List";
73
74
75
76
77 private static final String TABLE_MAP = MetisViewerResource.VIEWER_TABLE_MAP.getValue();
78
79
80
81
82 private static final String TABLE_STACKTRACE = MetisViewerResource.VIEWER_TABLE_STACKTRACE.getValue();
83
84
85
86
87 private final MetisViewerBuilder theBuilder;
88
89
90
91
92
93
94
95 protected MetisViewerFormatter(final OceanusDataFormatter pFormatter) throws OceanusException {
96 theBuilder = new MetisViewerBuilder(pFormatter);
97 }
98
99
100
101
102
103
104 protected void formatPage(final MetisViewerPage pPage) {
105
106 theBuilder.resetDocument(pPage);
107
108
109 switch (pPage.getMode()) {
110 case CONTENTS:
111 formatHTMLObject(pPage.getObject());
112 break;
113 case SUMMARY:
114 formatHTMLCollection(pPage.getObject(), pPage.getItemNo());
115 break;
116 case ITEMS:
117 formatHTMLItem(pPage.getObject(), pPage.getItemNo());
118 break;
119 default:
120 break;
121 }
122
123
124 theBuilder.formatDocument();
125 }
126
127
128
129
130
131
132 private void formatHTMLObject(final Object pObject) {
133
134 final Object myObject = pObject instanceof MetisDataDelta myDelta
135 ? myDelta.getObject()
136 : pObject;
137
138
139 if (myObject instanceof MetisFieldItem myField) {
140 formatHTMLEosFieldItem(myField);
141
142
143 } else if (myObject instanceof StackTraceElement[] myStack) {
144 formatHTMLStackTrace(myStack);
145
146
147 } else if (myObject instanceof OceanusProfile myProfile) {
148 formatHTMLEosFieldItem(new MetisViewerProfileWrapper(myProfile));
149
150
151 } else if (myObject instanceof Throwable myThrow) {
152 formatHTMLEosFieldItem(new MetisViewerExceptionWrapper(myThrow));
153
154
155 } else {
156 formatHTMLUnknown(myObject);
157 }
158 }
159
160
161
162
163
164
165
166 private void formatHTMLCollection(final Object pObject,
167 final int pStart) {
168
169 Object myObject = pObject instanceof MetisDataDelta myDelta
170 ? myDelta.getObject()
171 : pObject;
172
173
174 if (myObject instanceof MetisDataList<?> myList) {
175 myObject = myList.getUnderlyingList();
176 } else if (myObject instanceof MetisDataMap<?, ?> myMap) {
177 myObject = myMap.getUnderlyingMap();
178 }
179
180
181 if (myObject instanceof List<?> myList) {
182 formatHTMLListSection(myList, pStart);
183
184
185 } else if (myObject instanceof Map<?, ?> myMap) {
186 formatHTMLMapSection(myMap, pStart);
187 }
188 }
189
190
191
192
193
194
195
196 private void formatHTMLItem(final Object pObject,
197 final int pIndex) {
198
199 Object myObject = pObject instanceof MetisDataDelta myDelta
200 ? myDelta.getObject()
201 : pObject;
202
203
204 if (myObject instanceof MetisDataList<?> myList) {
205 myObject = myList.getUnderlyingList();
206 }
207
208
209 if (myObject instanceof List<?> myList) {
210 formatHTMLListItem(myList, pIndex);
211 }
212 }
213
214
215
216
217
218
219 private void formatHTMLEosFieldItem(final MetisFieldItem pItem) {
220
221 final MetisFieldSetDef myFields = pItem.getDataFieldSet();
222 final boolean isVersioned = pItem instanceof MetisFieldVersionedItem;
223 final MetisFieldVersionedItem myItem = isVersioned
224 ? (MetisFieldVersionedItem) pItem
225 : null;
226
227
228 theBuilder.newTitle(myFields.getName());
229 theBuilder.newTable();
230 theBuilder.newTitleCell(COLUMN_FIELD);
231 theBuilder.newTitleCell(COLUMN_VALUE);
232
233
234 final Iterator<MetisFieldDef> myIterator = myFields.fieldIterator();
235 while (myIterator.hasNext()) {
236
237 final MetisFieldDef myField = myIterator.next();
238
239
240 final Object myValue = myField.isCalculated()
241 ? MetisDataFieldValue.SKIP
242 : myField.getFieldValue(pItem);
243
244
245 if (skipValue(myValue)) {
246 continue;
247 }
248
249
250 theBuilder.newTableRow();
251 theBuilder.newDataCell(myField.getFieldId().getId());
252 if (myItem != null
253 && myField instanceof MetisFieldVersionedDef
254 && myItem.fieldChanged(myField).isDifferent()) {
255 theBuilder.newDataCell(myValue, true);
256 } else {
257 theBuilder.newDataCell(myValue);
258 }
259 }
260 }
261
262
263
264
265
266
267
268 private static boolean skipValue(final Object pValue) {
269
270 Object myValue = pValue;
271
272
273 if (myValue instanceof MetisDataList<?> myList) {
274 myValue = myList.getUnderlyingList();
275 }
276 if (myValue instanceof List<?> myList) {
277 return myList.isEmpty();
278 }
279
280
281 if (myValue instanceof MetisDataMap<?, ?> myMap) {
282 myValue = myMap.getUnderlyingMap();
283 }
284 if (myValue instanceof Map<?, ?> myMap) {
285 return myMap.isEmpty();
286 }
287
288
289 if (myValue instanceof MetisFieldVersionHistory myHistory) {
290 return !myHistory.hasHistory();
291 }
292 if (myValue instanceof MetisFieldValidation myValid) {
293 return !myValid.hasErrors();
294 }
295
296
297 if (myValue instanceof OceanusDecimal myDecimal) {
298 return myDecimal.isZero();
299 }
300 if (myValue instanceof Number myNumber) {
301 return myNumber.longValue() == 0;
302 }
303
304
305 if (myValue instanceof Boolean myBool) {
306 return !myBool;
307 }
308
309
310 return myValue == null
311 || MetisDataFieldValue.SKIP.equals(myValue);
312 }
313
314
315
316
317
318
319
320 private void formatHTMLListItem(final List<?> pList,
321 final int pIndex) {
322
323 final Object myObject = pList.get(pIndex - 1);
324
325
326 formatHTMLObject(myObject);
327 }
328
329
330
331
332
333
334
335 private void formatHTMLListSection(final List<?> pList,
336 final int pStart) {
337
338 theBuilder.newTitle(TABLE_LIST);
339 theBuilder.newTable();
340 theBuilder.newTitleCell(COLUMN_INDEX);
341 theBuilder.newTitleCell(COLUMN_VALUE);
342
343
344 if (!pList.isEmpty()) {
345
346 final int myStart = (pStart - 1) * ITEMS_PER_PAGE;
347
348
349 final Iterator<?> myIterator = pList.listIterator(myStart);
350
351
352 int myCount = ITEMS_PER_PAGE;
353 int myIndex = myStart + 1;
354 while (myIterator.hasNext()
355 && myCount-- > 0) {
356
357 final Object myObject = myIterator.next();
358
359
360 theBuilder.newTableRow();
361 theBuilder.newDataCell(myIndex++);
362 theBuilder.newDataCell(myObject);
363 }
364 }
365 }
366
367
368
369
370
371
372
373 private void formatHTMLMapSection(final Map<?, ?> pMap,
374 final int pStart) {
375
376
377 theBuilder.newTitle(TABLE_MAP);
378 theBuilder.newTable();
379 theBuilder.newTitleCell(COLUMN_INDEX);
380 theBuilder.newTitleCell(COLUMN_KEY);
381 theBuilder.newTitleCell(COLUMN_VALUE);
382
383
384 if (!pMap.isEmpty()) {
385
386 int myCount = (pStart - 1) * ITEMS_PER_PAGE;
387 int myIndex = myCount + 1;
388
389
390 final Iterator<?> myIterator = pMap.entrySet().iterator();
391 if (myCount > 0) {
392
393 while (myIterator.hasNext()
394 && myCount-- > 0) {
395 myIterator.next();
396 }
397 }
398
399
400 myCount = ITEMS_PER_PAGE;
401 while (myIterator.hasNext()
402 && (myCount-- > 0)) {
403
404 final Entry<?, ?> myEntry = (Entry<?, ?>) myIterator.next();
405
406
407 theBuilder.newTableRow();
408 theBuilder.newDataCell(myIndex++);
409 theBuilder.newDataCell(myEntry.getKey());
410 theBuilder.newDataCell(myEntry.getValue());
411 }
412 }
413 }
414
415
416
417
418
419
420 private void formatHTMLStackTrace(final StackTraceElement[] pStack) {
421
422 theBuilder.newTitle(TABLE_STACKTRACE);
423 theBuilder.newTable();
424 theBuilder.newTitleCell(TABLE_STACKTRACE);
425
426
427 for (StackTraceElement st : pStack) {
428
429 theBuilder.newTableRow();
430 theBuilder.newDataCell(st.toString());
431 }
432 }
433
434
435
436
437
438
439 private void formatHTMLUnknown(final Object pObject) {
440
441 theBuilder.newTitle("Unknown");
442 theBuilder.newTable();
443 theBuilder.newTitleCell(COLUMN_FIELD);
444 theBuilder.newTitleCell(COLUMN_VALUE);
445
446
447 theBuilder.newTableRow();
448 theBuilder.newDataCell("Class");
449 theBuilder.newDataCell(pObject != null
450 ? pObject.getClass()
451 : null);
452
453
454 theBuilder.newTableRow();
455 theBuilder.newDataCell("Value");
456 theBuilder.newDataCell(pObject);
457 }
458 }