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