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