1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.oscal.lib.profile.resolver.selection;
28
29 import gov.nist.secauto.metaschema.model.common.metapath.MetapathExpression;
30 import gov.nist.secauto.metaschema.model.common.metapath.format.IPathFormatter;
31 import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueAssemblyNodeItem;
32 import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem;
33 import gov.nist.secauto.metaschema.model.common.util.ObjectUtils;
34 import gov.nist.secauto.oscal.lib.model.CatalogGroup;
35 import gov.nist.secauto.oscal.lib.model.Control;
36 import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer;
37
38 import org.apache.commons.lang3.tuple.Pair;
39
40 import java.util.Map;
41 import java.util.concurrent.ConcurrentHashMap;
42
43 import edu.umd.cs.findbugs.annotations.NonNull;
44 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
45
46 public class ControlSelectionState implements IControlSelectionState {
47 private static final MetapathExpression GROUP_CHILDREN = MetapathExpression.compile("group|descendant::control");
48
49 @NonNull
50 private final IIndexer index;
51 @NonNull
52 private final IControlFilter filter;
53 @NonNull
54 private final Map<IRequiredValueModelNodeItem, SelectionState> itemSelectionState = new ConcurrentHashMap<>();
55
56 @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "provides intentional access to index state")
57 public ControlSelectionState(@NonNull IIndexer index, @NonNull IControlFilter filter) {
58 this.index = index;
59 this.filter = filter;
60 }
61
62 @Override
63 @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "provides intentional access to index state")
64 public IIndexer getIndex() {
65 return index;
66 }
67
68 @NonNull
69 public IControlFilter getFilter() {
70 return filter;
71 }
72
73 @Override
74 public boolean isSelected(@NonNull IRequiredValueModelNodeItem item) {
75 return getSelectionState(item).isSelected();
76 }
77
78 @NonNull
79 protected SelectionState getSelectionState(@NonNull IRequiredValueModelNodeItem item) {
80 SelectionState retval = itemSelectionState.get(item);
81 if (retval == null) {
82 Object itemValue = ObjectUtils.requireNonNull(item.getValue());
83
84 if (itemValue instanceof Control) {
85 Control control = (Control) itemValue;
86
87
88 IRequiredValueAssemblyNodeItem parentItem = ObjectUtils.requireNonNull(item.getParentContentNodeItem());
89 Object parentValue = parentItem.getValue();
90 Control parentControl = parentValue instanceof Control ? (Control) parentValue : null;
91
92 boolean defaultMatch = false;
93 if (parentControl != null) {
94 SelectionState parentSelectionState = getSelectionState(parentItem);
95 defaultMatch = parentSelectionState.isSelected() && parentSelectionState.isWithChildren();
96 }
97
98 Pair<Boolean, Boolean> matchResult = getFilter().match(control, defaultMatch);
99 boolean selected = matchResult.getLeft();
100 boolean withChildren = matchResult.getRight();
101
102 retval = new SelectionState(selected, withChildren);
103
104 } else if (itemValue instanceof CatalogGroup) {
105
106 boolean selected = GROUP_CHILDREN.evaluate(item).asStream()
107 .map(child -> {
108 return getSelectionState((IRequiredValueModelNodeItem) ObjectUtils.requireNonNull(child)).isSelected();
109 })
110 .reduce(false, (first, second) -> first || second);
111
112 retval = new SelectionState(selected, false);
113 } else {
114 throw new IllegalStateException(
115 String.format("Selection not supported for type '%s' at path '%s'",
116 itemValue.getClass().getName(),
117 item.toPath(IPathFormatter.METAPATH_PATH_FORMATER)));
118 }
119 itemSelectionState.put(item, retval);
120 }
121 return retval;
122 }
123
124 private static final class SelectionState {
125 private final boolean selected;
126 private final boolean withChildren;
127
128 private SelectionState(boolean selected, boolean withChildren) {
129 this.selected = selected;
130 this.withChildren = withChildren;
131 }
132
133 public boolean isSelected() {
134 return selected;
135 }
136
137 public boolean isWithChildren() {
138 return selected && withChildren;
139 }
140 }
141 }