1
2
3
4
5
6 package dev.metaschema.oscal.lib.profile.resolver.policy;
7
8 import com.vladsch.flexmark.ast.InlineLinkNode;
9 import com.vladsch.flexmark.util.ast.Node;
10
11 import org.apache.logging.log4j.LogManager;
12 import org.apache.logging.log4j.Logger;
13
14 import java.util.EnumSet;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Locale;
18 import java.util.Map;
19 import java.util.Set;
20 import java.util.UUID;
21 import java.util.function.BiConsumer;
22
23 import dev.metaschema.core.datatype.markup.IMarkupString;
24 import dev.metaschema.core.datatype.markup.flexmark.InsertAnchorExtension;
25 import dev.metaschema.core.datatype.markup.flexmark.InsertAnchorExtension.InsertAnchorNode;
26 import dev.metaschema.core.metapath.IMetapathExpression;
27 import dev.metaschema.core.metapath.format.IPathFormatter;
28 import dev.metaschema.core.metapath.item.atomic.IMarkupItem;
29 import dev.metaschema.core.metapath.item.node.IAssemblyNodeItem;
30 import dev.metaschema.core.metapath.item.node.IDocumentNodeItem;
31 import dev.metaschema.core.metapath.item.node.IFieldNodeItem;
32 import dev.metaschema.core.metapath.item.node.IModelNodeItem;
33 import dev.metaschema.core.qname.IEnhancedQName;
34 import dev.metaschema.core.util.CollectionUtil;
35 import dev.metaschema.core.util.ObjectUtils;
36 import dev.metaschema.oscal.lib.OscalBindingContext;
37 import dev.metaschema.oscal.lib.OscalModelConstants;
38 import dev.metaschema.oscal.lib.model.CatalogGroup;
39 import dev.metaschema.oscal.lib.model.Control;
40 import dev.metaschema.oscal.lib.model.ControlPart;
41 import dev.metaschema.oscal.lib.model.Link;
42 import dev.metaschema.oscal.lib.model.Property;
43 import dev.metaschema.oscal.lib.model.metadata.AbstractProperty;
44 import dev.metaschema.oscal.lib.model.metadata.IProperty;
45 import dev.metaschema.oscal.lib.profile.resolver.ProfileResolver.UriResolver;
46 import dev.metaschema.oscal.lib.profile.resolver.support.AbstractCatalogEntityVisitor;
47 import dev.metaschema.oscal.lib.profile.resolver.support.IEntityItem;
48 import dev.metaschema.oscal.lib.profile.resolver.support.IIndexer;
49 import edu.umd.cs.findbugs.annotations.NonNull;
50 import edu.umd.cs.findbugs.annotations.Nullable;
51 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
52
53 public final class ReferenceCountingVisitor
54 extends AbstractCatalogEntityVisitor<ReferenceCountingVisitor.Context, Void>
55 implements IReferenceVisitor<ReferenceCountingVisitor.Context> {
56 private static final Logger LOGGER = LogManager.getLogger(ReferenceCountingVisitor.class);
57
58 private static final ReferenceCountingVisitor SINGLETON = new ReferenceCountingVisitor();
59
60 @NonNull
61 private static final IMetapathExpression PARAM_MARKUP_METAPATH
62 = IMetapathExpression
63 .compile(
64 "label|usage|constraint/(description|tests/remarks)|guideline/prose|select/choice|remarks",
65 OscalBindingContext.OSCAL_STATIC_METAPATH_CONTEXT);
66 @NonNull
67 private static final IMetapathExpression ROLE_MARKUP_METAPATH
68 = IMetapathExpression.compile("title|description|remarks",
69 OscalBindingContext.OSCAL_STATIC_METAPATH_CONTEXT);
70 @NonNull
71 private static final IMetapathExpression LOCATION_MARKUP_METAPATH
72 = IMetapathExpression.compile("title|remarks",
73 OscalBindingContext.OSCAL_STATIC_METAPATH_CONTEXT);
74 @NonNull
75 private static final IMetapathExpression PARTY_MARKUP_METAPATH
76 = IMetapathExpression.compile("title|remarks",
77 OscalBindingContext.OSCAL_STATIC_METAPATH_CONTEXT);
78 @NonNull
79 private static final IMetapathExpression RESOURCE_MARKUP_METAPATH
80 = IMetapathExpression.compile("title|description|remarks",
81 OscalBindingContext.OSCAL_STATIC_METAPATH_CONTEXT);
82
83 @NonNull
84 private static final IReferencePolicy<Property> PROPERTY_POLICY_IGNORE = IReferencePolicy.ignore();
85 @NonNull
86 private static final IReferencePolicy<Link> LINK_POLICY_IGNORE = IReferencePolicy.ignore();
87
88 @NonNull
89 private static final Map<IEnhancedQName, IReferencePolicy<Property>> PROPERTY_POLICIES;
90 @NonNull
91 private static final Map<String, IReferencePolicy<Link>> LINK_POLICIES;
92 @NonNull
93 private static final InsertReferencePolicy INSERT_POLICY = new InsertReferencePolicy();
94 @NonNull
95 private static final AnchorReferencePolicy ANCHOR_POLICY = new AnchorReferencePolicy();
96
97 static {
98 PROPERTY_POLICIES = new HashMap<>();
99 PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "resolution-tool"), PROPERTY_POLICY_IGNORE);
100 PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "label"), PROPERTY_POLICY_IGNORE);
101 PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "sort-id"), PROPERTY_POLICY_IGNORE);
102 PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "alt-label"), PROPERTY_POLICY_IGNORE);
103 PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "alt-identifier"), PROPERTY_POLICY_IGNORE);
104 PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "method"), PROPERTY_POLICY_IGNORE);
105 PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "keep"), PROPERTY_POLICY_IGNORE);
106 PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.RMF_NAMESPACE, "method"), PROPERTY_POLICY_IGNORE);
107 PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.RMF_NAMESPACE, "aggregates"),
108 PropertyReferencePolicy.create(IIdentifierParser.IDENTITY_PARSER, IEntityItem.ItemType.PARAMETER));
109
110 LINK_POLICIES = new HashMap<>();
111 LINK_POLICIES.put("source-profile", LINK_POLICY_IGNORE);
112 LINK_POLICIES.put("citation", LinkReferencePolicy.create(IEntityItem.ItemType.RESOURCE));
113 LINK_POLICIES.put("reference", LinkReferencePolicy.create(IEntityItem.ItemType.RESOURCE));
114 LINK_POLICIES.put("related", LinkReferencePolicy.create(IEntityItem.ItemType.CONTROL));
115 LINK_POLICIES.put("required", LinkReferencePolicy.create(IEntityItem.ItemType.CONTROL));
116 LINK_POLICIES.put("corresp", LinkReferencePolicy.create(IEntityItem.ItemType.PART));
117 }
118
119 @SuppressFBWarnings(value = "SING_SINGLETON_GETTER_NOT_SYNCHRONIZED", justification = "class initialization")
120 public static ReferenceCountingVisitor instance() {
121 return SINGLETON;
122 }
123
124 private ReferenceCountingVisitor() {
125
126
127
128 super(ObjectUtils.notNull(EnumSet.complementOf(
129 EnumSet.of(
130 IEntityItem.ItemType.PART,
131 IEntityItem.ItemType.ROLE,
132 IEntityItem.ItemType.LOCATION,
133 IEntityItem.ItemType.PARTY,
134 IEntityItem.ItemType.PARAMETER,
135 IEntityItem.ItemType.RESOURCE))));
136 }
137
138 @Override
139 protected Void newDefaultResult(Context context) {
140
141 return null;
142 }
143
144 @Override
145 protected Void aggregateResults(Void first, Void second, Context context) {
146
147 return null;
148 }
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 public void visitCatalog(
168 @NonNull IDocumentNodeItem catalogItem,
169 @NonNull IIndexer indexer,
170 @NonNull UriResolver resolver) {
171 Context context = new Context(indexer, resolver);
172 visitCatalog(catalogItem, context);
173
174 IIndexer index = context.getIndexer();
175
176
177 IIndexer.getReferencedEntitiesAsStream(index.getEntitiesByItemType(IEntityItem.ItemType.ROLE))
178 .forEachOrdered(
179 item -> resolveEntity(ObjectUtils.notNull(item), context, ReferenceCountingVisitor::resolveRole));
180 IIndexer.getReferencedEntitiesAsStream(index.getEntitiesByItemType(IEntityItem.ItemType.LOCATION))
181 .forEachOrdered(
182 item -> resolveEntity(ObjectUtils.notNull(item), context,
183 ReferenceCountingVisitor::resolveLocation));
184 IIndexer.getReferencedEntitiesAsStream(index.getEntitiesByItemType(IEntityItem.ItemType.PARTY))
185 .forEachOrdered(
186 item -> resolveEntity(ObjectUtils.notNull(item), context,
187 ReferenceCountingVisitor::resolveParty));
188 IIndexer.getReferencedEntitiesAsStream(index.getEntitiesByItemType(IEntityItem.ItemType.PARAMETER))
189 .forEachOrdered(
190 item -> resolveEntity(ObjectUtils.notNull(item), context,
191 ReferenceCountingVisitor::resolveParameter));
192 IIndexer.getReferencedEntitiesAsStream(index.getEntitiesByItemType(IEntityItem.ItemType.RESOURCE))
193 .forEachOrdered(
194 item -> resolveEntity(ObjectUtils.notNull(item), context,
195 ReferenceCountingVisitor::resolveResource));
196 }
197
198 @Override
199 public Void visitGroup(
200 IAssemblyNodeItem item,
201 Void childResult,
202 Context context) {
203 IIndexer index = context.getIndexer();
204
205
206
207 if (IIndexer.SelectionStatus.SELECTED.equals(index.getSelectionStatus(item))) {
208 CatalogGroup group = ObjectUtils.requireNonNull((CatalogGroup) item.getValue());
209 String id = group.getId();
210
211 boolean resolve;
212 if (id == null) {
213
214 resolve = true;
215 } else {
216 IEntityItem entity = index.getEntity(IEntityItem.ItemType.GROUP, id, false);
217 if (entity != null && !context.isResolved(entity)) {
218
219 context.markResolved(entity);
220 resolve = true;
221 } else {
222 resolve = false;
223 }
224 }
225
226
227 if (resolve) {
228 resolveGroup(item, context);
229 }
230 }
231 return null;
232 }
233
234 @Override
235 public Void visitControl(
236 IAssemblyNodeItem item,
237 Void childResult,
238 Context context) {
239 IIndexer index = context.getIndexer();
240
241 if (IIndexer.SelectionStatus.SELECTED.equals(index.getSelectionStatus(item))) {
242 Control control = ObjectUtils.requireNonNull((Control) item.getValue());
243 IEntityItem entity
244 = context.getIndexer().getEntity(IEntityItem.ItemType.CONTROL, ObjectUtils.notNull(control.getId()), false);
245
246
247 assert entity != null;
248
249 if (!context.isResolved(entity)) {
250 context.markResolved(entity);
251 if (IIndexer.SelectionStatus.SELECTED.equals(context.getIndexer().getSelectionStatus(item))) {
252 resolveControl(item, context);
253 }
254 }
255 }
256 return null;
257 }
258
259 @Override
260 protected void visitParts(
261 IAssemblyNodeItem groupOrControlItem,
262 Context context) {
263
264 CHILD_PART_METAPATH.evaluate(groupOrControlItem).stream()
265 .map(item -> (IAssemblyNodeItem) item)
266 .forEachOrdered(partItem -> {
267 visitPart(ObjectUtils.notNull(partItem), groupOrControlItem, context);
268 });
269 }
270
271 @Override
272 protected void visitPart(
273 IAssemblyNodeItem item,
274 IAssemblyNodeItem groupOrControlItem,
275 Context context) {
276 assert context != null;
277
278 ControlPart part = ObjectUtils.requireNonNull((ControlPart) item.getValue());
279 String id = part.getId();
280
281 boolean resolve;
282 if (id == null) {
283
284 resolve = true;
285 } else {
286 IEntityItem entity = context.getIndexer().getEntity(IEntityItem.ItemType.PART, id, false);
287 if (entity != null && !context.isResolved(entity)) {
288
289 context.markResolved(entity);
290 resolve = true;
291 } else {
292 resolve = false;
293 }
294 }
295
296 if (resolve) {
297 resolvePart(item, context);
298 }
299 }
300
301 protected void resolveGroup(
302 @NonNull IAssemblyNodeItem item,
303 @NonNull Context context) {
304 if (IIndexer.SelectionStatus.SELECTED.equals(context.getIndexer().getSelectionStatus(item))) {
305
306
307 item.getModelItemsByName(OscalModelConstants.QNAME_TITLE)
308 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
309 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
310 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
311 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
312 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
313
314
315 visitParts(item, context);
316
317
318 }
319 }
320
321 protected void resolveControl(
322 @NonNull IAssemblyNodeItem item,
323 @NonNull Context context) {
324
325 item.getModelItemsByName(OscalModelConstants.QNAME_TITLE)
326 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
327 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
328 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
329 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
330 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
331
332
333 visitParts(item, context);
334
335
336 }
337
338 private static void resolveRole(@NonNull IEntityItem entity, @NonNull Context context) {
339 IModelNodeItem<?, ?> item = entity.getInstance();
340 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
341 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
342 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
343 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
344 ROLE_MARKUP_METAPATH.evaluate(item)
345 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
346 }
347
348 private static void resolveParty(@NonNull IEntityItem entity, @NonNull Context context) {
349 IModelNodeItem<?, ?> item = entity.getInstance();
350 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
351 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
352 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
353 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
354 PARTY_MARKUP_METAPATH.evaluate(item)
355 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
356 }
357
358 public static void resolveLocation(@NonNull IEntityItem entity, @NonNull Context context) {
359 IModelNodeItem<?, ?> item = entity.getInstance();
360 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
361 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
362 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
363 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
364 LOCATION_MARKUP_METAPATH.evaluate(item)
365 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
366 }
367
368 public static void resolveResource(@NonNull IEntityItem entity, @NonNull Context context) {
369 IModelNodeItem<?, ?> item = entity.getInstance();
370
371 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
372 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
373
374 item.getModelItemsByName(OscalModelConstants.QNAME_CITATION).forEach(child -> {
375 if (child != null) {
376 child.getModelItemsByName(OscalModelConstants.QNAME_TEXT)
377 .forEach(citationChild -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) citationChild), context));
378 child.getModelItemsByName(OscalModelConstants.QNAME_PROP)
379 .forEach(citationChild -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) citationChild), context));
380 child.getModelItemsByName(OscalModelConstants.QNAME_LINK)
381 .forEach(citationChild -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) citationChild), context));
382 }
383 });
384
385 RESOURCE_MARKUP_METAPATH.evaluate(item)
386 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
387 }
388
389 public static void resolveParameter(@NonNull IEntityItem entity, @NonNull Context context) {
390 IModelNodeItem<?, ?> item = entity.getInstance();
391
392 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
393 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
394 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
395 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
396 PARAM_MARKUP_METAPATH.evaluate(item)
397 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
398 }
399
400 private static void resolvePart(
401 @NonNull IAssemblyNodeItem item,
402 @NonNull Context context) {
403 item.getModelItemsByName(OscalModelConstants.QNAME_TITLE)
404 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
405 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
406 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
407 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
408 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
409 item.getModelItemsByName(OscalModelConstants.QNAME_PROSE)
410 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
411
412
413
414 }
415
416 private static void handleMarkup(
417 @NonNull IFieldNodeItem item,
418 @NonNull Context context) {
419 IMarkupItem markupItem = (IMarkupItem) item.toAtomicItem();
420 IMarkupString<?> markup = markupItem.asMarkup();
421 handleMarkup(item, markup, context);
422 }
423
424 private static void handleMarkup(
425 @NonNull IFieldNodeItem contextItem,
426 @NonNull IMarkupString<?> text,
427 @NonNull Context context) {
428 for (Node node : CollectionUtil.toIterable(
429 ObjectUtils.notNull(text.getNodesAsStream().iterator()))) {
430 if (node instanceof InsertAnchorExtension.InsertAnchorNode) {
431 handleInsert(contextItem, (InsertAnchorNode) node, context);
432 } else if (node instanceof InlineLinkNode) {
433 handleAnchor(contextItem, (InlineLinkNode) node, context);
434 }
435 }
436 }
437
438 private static void handleInsert(
439 @NonNull IFieldNodeItem contextItem,
440 @NonNull InsertAnchorExtension.InsertAnchorNode node,
441 @NonNull Context context) {
442 boolean retval = INSERT_POLICY.handleReference(contextItem, node, context);
443 if (LOGGER.isDebugEnabled() && !retval) {
444 LOGGER.atDebug().log("Unsupported insert type '{}' at '{}'",
445 node.getType().toString(),
446 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
447 }
448 }
449
450 private static void handleAnchor(
451 @NonNull IFieldNodeItem contextItem,
452 @NonNull InlineLinkNode node,
453 @NonNull Context context) {
454 boolean result = ANCHOR_POLICY.handleReference(contextItem, node, context);
455 if (LOGGER.isDebugEnabled() && !result) {
456 LOGGER.atDebug().log("Unsupported anchor with href '{}' at '{}'",
457 node.getUrl().toString(),
458 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
459 }
460 }
461
462 private static void handleProperty(
463 @NonNull IAssemblyNodeItem item,
464 @NonNull Context context) {
465 Property property = ObjectUtils.requireNonNull((Property) item.getValue());
466 IEnhancedQName qname = property.getQName();
467
468 IReferencePolicy<Property> policy = PROPERTY_POLICIES.get(qname);
469
470 boolean result = policy != null && policy.handleReference(item, property, context);
471 if (LOGGER.isDebugEnabled() && !result) {
472 LOGGER.atDebug().log("Unsupported property '{}' at '{}'",
473 property.getQName(),
474 item.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
475 }
476 }
477
478 private static void handleLink(
479 @NonNull IAssemblyNodeItem item,
480 @NonNull Context context) {
481 Link link = ObjectUtils.requireNonNull((Link) item.getValue());
482 IReferencePolicy<Link> policy = null;
483 String rel = link.getRel();
484 if (rel != null) {
485 policy = LINK_POLICIES.get(rel);
486 }
487
488 boolean result = policy != null && policy.handleReference(item, link, context);
489 if (LOGGER.isDebugEnabled() && !result) {
490 LOGGER.atDebug().log("unsupported link rel '{}' at '{}'",
491 link.getRel(),
492 item.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
493 }
494 }
495
496 protected static void resolveEntity(
497 @NonNull IEntityItem entity,
498 @NonNull Context context,
499 @NonNull BiConsumer<IEntityItem, Context> handler) {
500
501 if (!context.isResolved(entity)) {
502 context.markResolved(entity);
503
504 if (LOGGER.isDebugEnabled()) {
505 LOGGER.atDebug().log("Resolving {} identified as '{}'",
506 entity.getItemType().name(),
507 entity.getIdentifier());
508 }
509
510 if (!IIndexer.SelectionStatus.UNSELECTED
511 .equals(context.getIndexer().getSelectionStatus(entity.getInstance()))) {
512
513 handler.accept(entity, context);
514 }
515 }
516 }
517
518 public void resolveEntity(
519 @NonNull IEntityItem entity,
520 @NonNull Context context) {
521 resolveEntity(entity, context, (theEntity, theContext) -> entityDispatch(
522 ObjectUtils.notNull(theEntity),
523 ObjectUtils.notNull(theContext)));
524 }
525
526 protected void entityDispatch(@NonNull IEntityItem entity, @NonNull Context context) {
527 IAssemblyNodeItem item = (IAssemblyNodeItem) entity.getInstance();
528 switch (entity.getItemType()) {
529 case CONTROL:
530 resolveControl(item, context);
531 break;
532 case GROUP:
533 resolveGroup(item, context);
534 break;
535 case LOCATION:
536 resolveLocation(entity, context);
537 break;
538 case PARAMETER:
539 resolveParameter(entity, context);
540 break;
541 case PART:
542 resolvePart(item, context);
543 break;
544 case PARTY:
545 resolveParty(entity, context);
546 break;
547 case RESOURCE:
548 resolveResource(entity, context);
549 break;
550 case ROLE:
551 resolveRole(entity, context);
552 break;
553 default:
554 throw new UnsupportedOperationException(entity.getItemType().name());
555 }
556 }
557
558
559
560
561
562
563
564
565
566
567
568
569 public static final class Context {
570 @NonNull
571 private final IIndexer indexer;
572 @NonNull
573 private final UriResolver resolver;
574 @NonNull
575 private final Set<IEntityItem> resolvedEntities = new HashSet<>();
576
577 private Context(@NonNull IIndexer indexer, @NonNull UriResolver resolver) {
578 this.indexer = indexer;
579 this.resolver = resolver;
580 }
581
582 @NonNull
583 @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intending to expose this field")
584 public IIndexer getIndexer() {
585 return indexer;
586 }
587
588 @NonNull
589 public UriResolver getUriResolver() {
590 return resolver;
591 }
592
593 @Nullable
594 public IEntityItem getEntity(@NonNull IEntityItem.ItemType itemType, @NonNull String identifier) {
595 return getIndexer().getEntity(itemType, identifier);
596 }
597
598 public void markResolved(@NonNull IEntityItem entity) {
599 resolvedEntities.add(entity);
600 }
601
602 public boolean isResolved(@NonNull IEntityItem entity) {
603 return resolvedEntities.contains(entity);
604 }
605
606 public void incrementReferenceCount(
607 @NonNull IModelNodeItem<?, ?> contextItem,
608 @NonNull IEntityItem.ItemType type,
609 @NonNull UUID identifier) {
610 incrementReferenceCountInternal(
611 contextItem,
612 type,
613 ObjectUtils.notNull(identifier.toString()),
614 false);
615 }
616
617 public void incrementReferenceCount(
618 @NonNull IModelNodeItem<?, ?> contextItem,
619 @NonNull IEntityItem.ItemType type,
620 @NonNull String identifier) {
621 incrementReferenceCountInternal(
622 contextItem,
623 type,
624 identifier,
625 type.isUuid());
626 }
627
628 private void incrementReferenceCountInternal(
629 @NonNull IModelNodeItem<?, ?> contextItem,
630 @NonNull IEntityItem.ItemType type,
631 @NonNull String identifier,
632 boolean normalize) {
633 IEntityItem item = getIndexer().getEntity(type, identifier, normalize);
634 if (item == null) {
635 if (LOGGER.isErrorEnabled()) {
636 LOGGER.atError().log("Unknown reference to {} '{}' at '{}'",
637 type.toString().toLowerCase(Locale.ROOT),
638 identifier,
639 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
640 }
641 } else {
642 item.incrementReferenceCount();
643 }
644 }
645 }
646 }