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