1
2
3
4
5
6 package dev.metaschema.oscal.lib.profile.resolver.selection;
7
8 import org.apache.logging.log4j.LogManager;
9 import org.apache.logging.log4j.Logger;
10
11 import dev.metaschema.core.metapath.item.node.IAssemblyNodeItem;
12 import dev.metaschema.core.metapath.item.node.IDocumentNodeItem;
13 import dev.metaschema.core.metapath.item.node.IRootAssemblyNodeItem;
14 import dev.metaschema.core.util.ObjectUtils;
15 import dev.metaschema.oscal.lib.model.Catalog;
16 import dev.metaschema.oscal.lib.model.CatalogGroup;
17 import dev.metaschema.oscal.lib.model.Control;
18 import dev.metaschema.oscal.lib.model.ControlPart;
19 import dev.metaschema.oscal.lib.profile.resolver.support.AbstractIndexingVisitor;
20 import dev.metaschema.oscal.lib.profile.resolver.support.IEntityItem;
21 import dev.metaschema.oscal.lib.profile.resolver.support.IIndexer;
22 import dev.metaschema.oscal.lib.profile.resolver.support.IIndexer.SelectionStatus;
23 import edu.umd.cs.findbugs.annotations.NonNull;
24 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public final class ControlSelectionVisitor
49 extends AbstractIndexingVisitor<IControlSelectionState, Boolean> {
50 private static final Logger LOGGER = LogManager.getLogger(ControlSelectionVisitor.class);
51
52 private static final ControlSelectionVisitor SINGLETON = new ControlSelectionVisitor();
53
54 @SuppressFBWarnings(value = "SING_SINGLETON_GETTER_NOT_SYNCHRONIZED", justification = "class initialization")
55 public static ControlSelectionVisitor instance() {
56 return SINGLETON;
57 }
58
59 private ControlSelectionVisitor() {
60
61 }
62
63 @Override
64 protected IIndexer getIndexer(IControlSelectionState state) {
65 return state.getIndex();
66 }
67
68 @Override
69 protected Boolean newDefaultResult(IControlSelectionState state) {
70 return false;
71 }
72
73 @Override
74 protected Boolean aggregateResults(Boolean first, Boolean second, IControlSelectionState state) {
75 return first || second;
76 }
77
78 public void visit(@NonNull IDocumentNodeItem catalogDocument, @NonNull IControlSelectionState state) {
79 visitCatalog(catalogDocument, state);
80 }
81
82 public void visitProfile(
83 @NonNull IDocumentNodeItem catalogDocument,
84 @NonNull IDocumentNodeItem profileDocument,
85 @NonNull IControlSelectionState state) {
86 visit(catalogDocument, state);
87
88 profileDocument.modelItems().forEachOrdered(item -> {
89 IRootAssemblyNodeItem root = ObjectUtils.requireNonNull((IRootAssemblyNodeItem) item);
90
91 visitMetadata(root, state);
92 visitBackMatter(root, state);
93 });
94 }
95
96 @Override
97 public Boolean visitCatalog(IDocumentNodeItem catalogDocument, IControlSelectionState state) {
98 getIndexer(state).setSelectionStatus(catalogDocument, SelectionStatus.SELECTED);
99 return super.visitCatalog(catalogDocument, state);
100 }
101
102 @Override
103 public Boolean visitGroup(IAssemblyNodeItem groupItem, Boolean childSelected,
104 IControlSelectionState state) {
105 super.visitGroup(groupItem, childSelected, state);
106 if (LOGGER.isTraceEnabled()) {
107 CatalogGroup group = ObjectUtils.requireNonNull((CatalogGroup) groupItem.getValue());
108 LOGGER.atTrace().log("Selecting group '{}'. match={}", group.getId(), childSelected);
109 }
110
111
112 assert state.isSelected(groupItem) == childSelected;
113
114 if (childSelected) {
115 getIndexer(state).setSelectionStatus(groupItem, SelectionStatus.SELECTED);
116 } else {
117 getIndexer(state).setSelectionStatus(groupItem, SelectionStatus.UNSELECTED);
118 }
119
120 handlePartSelection(groupItem, childSelected, state);
121 return childSelected;
122 }
123
124 private void handlePartSelection(
125 @NonNull IAssemblyNodeItem groupOrControlItem,
126 boolean selected,
127 IControlSelectionState state) {
128 if (isVisitedItemType(IEntityItem.ItemType.PART)) {
129 SelectionStatus selectionStatus = selected ? SelectionStatus.SELECTED : SelectionStatus.UNSELECTED;
130
131 IIndexer index = getIndexer(state);
132 CHILD_PART_METAPATH.evaluate(groupOrControlItem).stream()
133 .map(item -> (IAssemblyNodeItem) item)
134 .forEachOrdered(partItem -> {
135 index.setSelectionStatus(ObjectUtils.requireNonNull(partItem), selectionStatus);
136 });
137 }
138 }
139
140 @Override
141 public Boolean visitControl(
142 IAssemblyNodeItem controlItem,
143 Boolean childResult,
144 IControlSelectionState state) {
145 super.visitControl(controlItem, childResult, state);
146
147 boolean selected = state.isSelected(controlItem);
148 if (selected) {
149 getIndexer(state).setSelectionStatus(controlItem, SelectionStatus.SELECTED);
150 } else {
151 getIndexer(state).setSelectionStatus(controlItem, SelectionStatus.UNSELECTED);
152 }
153
154 handlePartSelection(controlItem, selected, state);
155 return selected;
156 }
157 }