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.IMetapathExpression;
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.ProfileResolver.UriResolver;
34 import gov.nist.secauto.oscal.lib.profile.resolver.support.AbstractCatalogEntityVisitor;
35 import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem;
36 import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer;
37
38 import org.apache.logging.log4j.LogManager;
39 import org.apache.logging.log4j.Logger;
40
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 IMetapathExpression PARAM_MARKUP_METAPATH
63 = IMetapathExpression
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 IMetapathExpression ROLE_MARKUP_METAPATH
69 = IMetapathExpression.compile("title|description|remarks",
70 OscalBindingContext.OSCAL_STATIC_METAPATH_CONTEXT);
71 @NonNull
72 private static final IMetapathExpression LOCATION_MARKUP_METAPATH
73 = IMetapathExpression.compile("title|remarks",
74 OscalBindingContext.OSCAL_STATIC_METAPATH_CONTEXT);
75 @NonNull
76 private static final IMetapathExpression PARTY_MARKUP_METAPATH
77 = IMetapathExpression.compile("title|remarks",
78 OscalBindingContext.OSCAL_STATIC_METAPATH_CONTEXT);
79 @NonNull
80 private static final IMetapathExpression RESOURCE_MARKUP_METAPATH
81 = IMetapathExpression.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(
169 @NonNull IDocumentNodeItem catalogItem,
170 @NonNull IIndexer indexer,
171 @NonNull UriResolver resolver) {
172 Context context = new Context(indexer, resolver);
173 visitCatalog(catalogItem, context);
174
175 IIndexer index = context.getIndexer();
176
177
178 IIndexer.getReferencedEntitiesAsStream(index.getEntitiesByItemType(IEntityItem.ItemType.ROLE))
179 .forEachOrdered(
180 item -> resolveEntity(ObjectUtils.notNull(item), context, ReferenceCountingVisitor::resolveRole));
181 IIndexer.getReferencedEntitiesAsStream(index.getEntitiesByItemType(IEntityItem.ItemType.LOCATION))
182 .forEachOrdered(
183 item -> resolveEntity(ObjectUtils.notNull(item), context,
184 ReferenceCountingVisitor::resolveLocation));
185 IIndexer.getReferencedEntitiesAsStream(index.getEntitiesByItemType(IEntityItem.ItemType.PARTY))
186 .forEachOrdered(
187 item -> resolveEntity(ObjectUtils.notNull(item), context,
188 ReferenceCountingVisitor::resolveParty));
189 IIndexer.getReferencedEntitiesAsStream(index.getEntitiesByItemType(IEntityItem.ItemType.PARAMETER))
190 .forEachOrdered(
191 item -> resolveEntity(ObjectUtils.notNull(item), context,
192 ReferenceCountingVisitor::resolveParameter));
193 IIndexer.getReferencedEntitiesAsStream(index.getEntitiesByItemType(IEntityItem.ItemType.RESOURCE))
194 .forEachOrdered(
195 item -> resolveEntity(ObjectUtils.notNull(item), context,
196 ReferenceCountingVisitor::resolveResource));
197 }
198
199 @Override
200 public Void visitGroup(
201 IAssemblyNodeItem item,
202 Void childResult,
203 Context context) {
204 IIndexer index = context.getIndexer();
205
206
207
208 if (IIndexer.SelectionStatus.SELECTED.equals(index.getSelectionStatus(item))) {
209 CatalogGroup group = ObjectUtils.requireNonNull((CatalogGroup) item.getValue());
210 String id = group.getId();
211
212 boolean resolve;
213 if (id == null) {
214
215 resolve = true;
216 } else {
217 IEntityItem entity = index.getEntity(IEntityItem.ItemType.GROUP, id, false);
218 if (entity != null && !context.isResolved(entity)) {
219
220 context.markResolved(entity);
221 resolve = true;
222 } else {
223 resolve = false;
224 }
225 }
226
227
228 if (resolve) {
229 resolveGroup(item, context);
230 }
231 }
232 return null;
233 }
234
235 @Override
236 public Void visitControl(
237 IAssemblyNodeItem item,
238 Void childResult,
239 Context context) {
240 IIndexer index = context.getIndexer();
241
242 if (IIndexer.SelectionStatus.SELECTED.equals(index.getSelectionStatus(item))) {
243 Control control = ObjectUtils.requireNonNull((Control) item.getValue());
244 IEntityItem entity
245 = context.getIndexer().getEntity(IEntityItem.ItemType.CONTROL, ObjectUtils.notNull(control.getId()), false);
246
247
248 assert entity != null;
249
250 if (!context.isResolved(entity)) {
251 context.markResolved(entity);
252 if (IIndexer.SelectionStatus.SELECTED.equals(context.getIndexer().getSelectionStatus(item))) {
253 resolveControl(item, context);
254 }
255 }
256 }
257 return null;
258 }
259
260 @Override
261 protected void visitParts(
262 IAssemblyNodeItem groupOrControlItem,
263 Context context) {
264
265 CHILD_PART_METAPATH.evaluate(groupOrControlItem).stream()
266 .map(item -> (IAssemblyNodeItem) item)
267 .forEachOrdered(partItem -> {
268 visitPart(ObjectUtils.notNull(partItem), groupOrControlItem, context);
269 });
270 }
271
272 @Override
273 protected void visitPart(
274 IAssemblyNodeItem item,
275 IAssemblyNodeItem groupOrControlItem,
276 Context context) {
277 assert context != null;
278
279 ControlPart part = ObjectUtils.requireNonNull((ControlPart) item.getValue());
280 String id = part.getId();
281
282 boolean resolve;
283 if (id == null) {
284
285 resolve = true;
286 } else {
287 IEntityItem entity = context.getIndexer().getEntity(IEntityItem.ItemType.PART, id, false);
288 if (entity != null && !context.isResolved(entity)) {
289
290 context.markResolved(entity);
291 resolve = true;
292 } else {
293 resolve = false;
294 }
295 }
296
297 if (resolve) {
298 resolvePart(item, context);
299 }
300 }
301
302 protected void resolveGroup(
303 @NonNull IAssemblyNodeItem item,
304 @NonNull Context context) {
305 if (IIndexer.SelectionStatus.SELECTED.equals(context.getIndexer().getSelectionStatus(item))) {
306
307
308 item.getModelItemsByName(OscalModelConstants.QNAME_TITLE)
309 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
310 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
311 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
312 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
313 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
314
315
316 visitParts(item, context);
317
318
319 }
320 }
321
322 protected void resolveControl(
323 @NonNull IAssemblyNodeItem item,
324 @NonNull Context context) {
325
326 item.getModelItemsByName(OscalModelConstants.QNAME_TITLE)
327 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
328 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
329 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
330 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
331 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
332
333
334 visitParts(item, context);
335
336
337 }
338
339 private static void resolveRole(@NonNull IEntityItem entity, @NonNull Context context) {
340 IModelNodeItem<?, ?> item = entity.getInstance();
341 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
342 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
343 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
344 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
345 ROLE_MARKUP_METAPATH.evaluate(item)
346 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
347 }
348
349 private static void resolveParty(@NonNull IEntityItem entity, @NonNull Context context) {
350 IModelNodeItem<?, ?> item = entity.getInstance();
351 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
352 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
353 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
354 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
355 PARTY_MARKUP_METAPATH.evaluate(item)
356 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
357 }
358
359 public static void resolveLocation(@NonNull IEntityItem entity, @NonNull Context context) {
360 IModelNodeItem<?, ?> item = entity.getInstance();
361 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
362 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
363 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
364 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
365 LOCATION_MARKUP_METAPATH.evaluate(item)
366 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
367 }
368
369 public static void resolveResource(@NonNull IEntityItem entity, @NonNull Context context) {
370 IModelNodeItem<?, ?> item = entity.getInstance();
371
372 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
373 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
374
375 item.getModelItemsByName(OscalModelConstants.QNAME_CITATION).forEach(child -> {
376 if (child != null) {
377 child.getModelItemsByName(OscalModelConstants.QNAME_TEXT)
378 .forEach(citationChild -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) citationChild), context));
379 child.getModelItemsByName(OscalModelConstants.QNAME_PROP)
380 .forEach(citationChild -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) citationChild), context));
381 child.getModelItemsByName(OscalModelConstants.QNAME_LINK)
382 .forEach(citationChild -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) citationChild), context));
383 }
384 });
385
386 RESOURCE_MARKUP_METAPATH.evaluate(item)
387 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
388 }
389
390 public static void resolveParameter(@NonNull IEntityItem entity, @NonNull Context context) {
391 IModelNodeItem<?, ?> item = entity.getInstance();
392
393 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
394 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
395 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
396 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
397 PARAM_MARKUP_METAPATH.evaluate(item)
398 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
399 }
400
401 private static void resolvePart(
402 @NonNull IAssemblyNodeItem item,
403 @NonNull Context context) {
404 item.getModelItemsByName(OscalModelConstants.QNAME_TITLE)
405 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
406 item.getModelItemsByName(OscalModelConstants.QNAME_PROP)
407 .forEach(child -> handleProperty(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
408 item.getModelItemsByName(OscalModelConstants.QNAME_LINK)
409 .forEach(child -> handleLink(ObjectUtils.notNull((IAssemblyNodeItem) child), context));
410 item.getModelItemsByName(OscalModelConstants.QNAME_PROSE)
411 .forEach(child -> handleMarkup(ObjectUtils.notNull((IFieldNodeItem) child), context));
412
413
414
415 }
416
417 private static void handleMarkup(
418 @NonNull IFieldNodeItem item,
419 @NonNull Context context) {
420 IMarkupItem markupItem = (IMarkupItem) item.toAtomicItem();
421 IMarkupString<?> markup = markupItem.asMarkup();
422 handleMarkup(item, markup, context);
423 }
424
425 private static void handleMarkup(
426 @NonNull IFieldNodeItem contextItem,
427 @NonNull IMarkupString<?> text,
428 @NonNull Context context) {
429 for (Node node : CollectionUtil.toIterable(
430 ObjectUtils.notNull(text.getNodesAsStream().iterator()))) {
431 if (node instanceof InsertAnchorExtension.InsertAnchorNode) {
432 handleInsert(contextItem, (InsertAnchorNode) node, context);
433 } else if (node instanceof InlineLinkNode) {
434 handleAnchor(contextItem, (InlineLinkNode) node, context);
435 }
436 }
437 }
438
439 private static void handleInsert(
440 @NonNull IFieldNodeItem contextItem,
441 @NonNull InsertAnchorExtension.InsertAnchorNode node,
442 @NonNull Context context) {
443 boolean retval = INSERT_POLICY.handleReference(contextItem, node, context);
444 if (LOGGER.isDebugEnabled() && !retval) {
445 LOGGER.atDebug().log("Unsupported insert type '{}' at '{}'",
446 node.getType().toString(),
447 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
448 }
449 }
450
451 private static void handleAnchor(
452 @NonNull IFieldNodeItem contextItem,
453 @NonNull InlineLinkNode node,
454 @NonNull Context context) {
455 boolean result = ANCHOR_POLICY.handleReference(contextItem, node, context);
456 if (LOGGER.isDebugEnabled() && !result) {
457 LOGGER.atDebug().log("Unsupported anchor with href '{}' at '{}'",
458 node.getUrl().toString(),
459 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
460 }
461 }
462
463 private static void handleProperty(
464 @NonNull IAssemblyNodeItem item,
465 @NonNull Context context) {
466 Property property = ObjectUtils.requireNonNull((Property) item.getValue());
467 IEnhancedQName qname = property.getQName();
468
469 IReferencePolicy<Property> policy = PROPERTY_POLICIES.get(qname);
470
471 boolean result = policy != null && policy.handleReference(item, property, context);
472 if (LOGGER.isDebugEnabled() && !result) {
473 LOGGER.atDebug().log("Unsupported property '{}' at '{}'",
474 property.getQName(),
475 item.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
476 }
477 }
478
479 private static void handleLink(
480 @NonNull IAssemblyNodeItem item,
481 @NonNull Context context) {
482 Link link = ObjectUtils.requireNonNull((Link) item.getValue());
483 IReferencePolicy<Link> policy = null;
484 String rel = link.getRel();
485 if (rel != null) {
486 policy = LINK_POLICIES.get(rel);
487 }
488
489 boolean result = policy != null && policy.handleReference(item, link, context);
490 if (LOGGER.isDebugEnabled() && !result) {
491 LOGGER.atDebug().log("unsupported link rel '{}' at '{}'",
492 link.getRel(),
493 item.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
494 }
495 }
496
497 protected static void resolveEntity(
498 @NonNull IEntityItem entity,
499 @NonNull Context context,
500 @NonNull BiConsumer<IEntityItem, Context> handler) {
501
502 if (!context.isResolved(entity)) {
503 context.markResolved(entity);
504
505 if (LOGGER.isDebugEnabled()) {
506 LOGGER.atDebug().log("Resolving {} identified as '{}'",
507 entity.getItemType().name(),
508 entity.getIdentifier());
509 }
510
511 if (!IIndexer.SelectionStatus.UNSELECTED
512 .equals(context.getIndexer().getSelectionStatus(entity.getInstance()))) {
513
514 handler.accept(entity, context);
515 }
516 }
517 }
518
519 public void resolveEntity(
520 @NonNull IEntityItem entity,
521 @NonNull Context context) {
522 resolveEntity(entity, context, (theEntity, theContext) -> entityDispatch(
523 ObjectUtils.notNull(theEntity),
524 ObjectUtils.notNull(theContext)));
525 }
526
527 protected void entityDispatch(@NonNull IEntityItem entity, @NonNull Context context) {
528 IAssemblyNodeItem item = (IAssemblyNodeItem) entity.getInstance();
529 switch (entity.getItemType()) {
530 case CONTROL:
531 resolveControl(item, context);
532 break;
533 case GROUP:
534 resolveGroup(item, context);
535 break;
536 case LOCATION:
537 resolveLocation(entity, context);
538 break;
539 case PARAMETER:
540 resolveParameter(entity, context);
541 break;
542 case PART:
543 resolvePart(item, context);
544 break;
545 case PARTY:
546 resolveParty(entity, context);
547 break;
548 case RESOURCE:
549 resolveResource(entity, context);
550 break;
551 case ROLE:
552 resolveRole(entity, context);
553 break;
554 default:
555 throw new UnsupportedOperationException(entity.getItemType().name());
556 }
557 }
558
559
560
561
562
563
564
565
566
567
568
569
570 public static final class Context {
571 @NonNull
572 private final IIndexer indexer;
573 @NonNull
574 private final UriResolver resolver;
575 @NonNull
576 private final Set<IEntityItem> resolvedEntities = new HashSet<>();
577
578 private Context(@NonNull IIndexer indexer, @NonNull UriResolver resolver) {
579 this.indexer = indexer;
580 this.resolver = resolver;
581 }
582
583 @NonNull
584 @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intending to expose this field")
585 public IIndexer getIndexer() {
586 return indexer;
587 }
588
589 @NonNull
590 public UriResolver getUriResolver() {
591 return resolver;
592 }
593
594 @Nullable
595 public IEntityItem getEntity(@NonNull IEntityItem.ItemType itemType, @NonNull String identifier) {
596 return getIndexer().getEntity(itemType, identifier);
597 }
598
599 public void markResolved(@NonNull IEntityItem entity) {
600 resolvedEntities.add(entity);
601 }
602
603 public boolean isResolved(@NonNull IEntityItem entity) {
604 return resolvedEntities.contains(entity);
605 }
606
607 public void incrementReferenceCount(
608 @NonNull IModelNodeItem<?, ?> contextItem,
609 @NonNull IEntityItem.ItemType type,
610 @NonNull UUID identifier) {
611 incrementReferenceCountInternal(
612 contextItem,
613 type,
614 ObjectUtils.notNull(identifier.toString()),
615 false);
616 }
617
618 public void incrementReferenceCount(
619 @NonNull IModelNodeItem<?, ?> contextItem,
620 @NonNull IEntityItem.ItemType type,
621 @NonNull String identifier) {
622 incrementReferenceCountInternal(
623 contextItem,
624 type,
625 identifier,
626 type.isUuid());
627 }
628
629 private void incrementReferenceCountInternal(
630 @NonNull IModelNodeItem<?, ?> contextItem,
631 @NonNull IEntityItem.ItemType type,
632 @NonNull String identifier,
633 boolean normalize) {
634 IEntityItem item = getIndexer().getEntity(type, identifier, normalize);
635 if (item == null) {
636 if (LOGGER.isErrorEnabled()) {
637 LOGGER.atError().log("Unknown reference to {} '{}' at '{}'",
638 type.toString().toLowerCase(Locale.ROOT),
639 identifier,
640 contextItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER));
641 }
642 } else {
643 item.incrementReferenceCount();
644 }
645 }
646 }
647 }