1
2
3
4
5
6 package gov.nist.secauto.oscal.lib.profile.resolver.selection;
7
8 import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem;
9 import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
10 import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
11 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
12 import gov.nist.secauto.oscal.lib.OscalModelConstants;
13 import gov.nist.secauto.oscal.lib.model.BackMatter;
14 import gov.nist.secauto.oscal.lib.model.BackMatter.Resource;
15 import gov.nist.secauto.oscal.lib.model.Catalog;
16 import gov.nist.secauto.oscal.lib.model.CatalogGroup;
17 import gov.nist.secauto.oscal.lib.model.Control;
18 import gov.nist.secauto.oscal.lib.model.ControlPart;
19 import gov.nist.secauto.oscal.lib.model.Metadata;
20 import gov.nist.secauto.oscal.lib.model.Metadata.Location;
21 import gov.nist.secauto.oscal.lib.model.Metadata.Party;
22 import gov.nist.secauto.oscal.lib.model.Metadata.Role;
23 import gov.nist.secauto.oscal.lib.model.Parameter;
24 import gov.nist.secauto.oscal.lib.profile.resolver.support.AbstractCatalogEntityVisitor;
25 import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem;
26 import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem.ItemType;
27 import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer;
28 import gov.nist.secauto.oscal.lib.profile.resolver.support.IIndexer.SelectionStatus;
29
30 import org.apache.logging.log4j.LogManager;
31 import org.apache.logging.log4j.Logger;
32
33 import java.util.EnumSet;
34 import java.util.stream.Collectors;
35
36 import edu.umd.cs.findbugs.annotations.NonNull;
37 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
38
39 public class FilterNonSelectedVisitor
40 extends AbstractCatalogEntityVisitor<FilterNonSelectedVisitor.Context, DefaultResult> {
41 private static final Logger LOGGER = LogManager.getLogger(FilterNonSelectedVisitor.class);
42 @NonNull
43 private static final FilterNonSelectedVisitor SINGLETON = new FilterNonSelectedVisitor();
44
45 @NonNull
46 @SuppressFBWarnings(value = "SING_SINGLETON_GETTER_NOT_SYNCHRONIZED", justification = "class initialization")
47 public static FilterNonSelectedVisitor instance() {
48 return SINGLETON;
49 }
50
51 @SuppressWarnings("null")
52
53 @SuppressFBWarnings(value = "SING_SINGLETON_HAS_NONPRIVATE_CONSTRUCTOR", justification = "allows for extension")
54 protected FilterNonSelectedVisitor() {
55
56 super(EnumSet.of(IEntityItem.ItemType.GROUP, IEntityItem.ItemType.CONTROL, IEntityItem.ItemType.PARAMETER));
57 }
58
59 public void visitCatalog(@NonNull IDocumentNodeItem catalogItem, @NonNull IIndexer indexer) {
60 Context context = new Context(indexer);
61 IResult result = visitCatalog(catalogItem, context);
62
63 Catalog catalog = (Catalog) INodeItem.toValue(catalogItem);
64 result.applyTo(catalog);
65
66 catalogItem.modelItems().forEachOrdered(root -> {
67 root.getModelItemsByName(OscalModelConstants.QNAME_METADATA).stream()
68 .map(child -> (IAssemblyNodeItem) child)
69 .forEachOrdered(child -> {
70 assert child != null;
71 visitMetadata(child, context);
72 });
73
74 root.getModelItemsByName(OscalModelConstants.QNAME_BACK_MATTER).stream()
75 .map(child -> (IAssemblyNodeItem) child)
76 .forEachOrdered(child -> {
77 assert child != null;
78 visitBackMatter(child, context);
79 });
80 });
81 }
82
83 @Override
84 protected DefaultResult newDefaultResult(Context state) {
85 return new DefaultResult();
86 }
87
88 @Override
89 protected DefaultResult aggregateResults(DefaultResult first, DefaultResult second, Context state) {
90 return first.append(ObjectUtils.notNull(second));
91 }
92
93 protected void visitMetadata(@NonNull IAssemblyNodeItem metadataItem, Context context) {
94 Metadata metadata = ObjectUtils.requireNonNull((Metadata) metadataItem.getValue());
95
96 IIndexer index = context.getIndexer();
97
98
99 for (IEntityItem entity : IIndexer.getUnreferencedEntitiesAsStream(index.getEntitiesByItemType(ItemType.ROLE))
100 .collect(Collectors.toList())) {
101 Role role = entity.getInstanceValue();
102 if (LOGGER.isDebugEnabled()) {
103 LOGGER.atDebug().log("Removing role '{}'", role.getId());
104 }
105 metadata.removeRole(role);
106 index.removeItem(entity);
107 }
108
109 for (IEntityItem entity : IIndexer.getUnreferencedEntitiesAsStream(index.getEntitiesByItemType(ItemType.PARTY))
110 .collect(Collectors.toList())) {
111 Party party = entity.getInstanceValue();
112 if (LOGGER.isDebugEnabled()) {
113 LOGGER.atDebug().log("Removing party '{}'", party.getUuid());
114 }
115 metadata.removeParty(party);
116 index.removeItem(entity);
117 }
118
119 for (IEntityItem entity : IIndexer.getUnreferencedEntitiesAsStream(index.getEntitiesByItemType(ItemType.LOCATION))
120 .collect(Collectors.toList())) {
121 Location location = entity.getInstanceValue();
122 if (LOGGER.isDebugEnabled()) {
123 LOGGER.atDebug().log("Removing location '{}'", location.getUuid());
124 }
125 metadata.removeLocation(location);
126 index.removeItem(entity);
127 }
128 }
129
130 @SuppressWarnings("static-method")
131 private void visitBackMatter(@NonNull IAssemblyNodeItem backMatterItem, Context context) {
132 BackMatter backMatter = ObjectUtils.requireNonNull((BackMatter) backMatterItem.getValue());
133
134 IIndexer index = context.getIndexer();
135 for (IEntityItem entity : IIndexer.getUnreferencedEntitiesAsStream(index.getEntitiesByItemType(ItemType.RESOURCE))
136 .collect(Collectors.toList())) {
137 Resource resource = entity.getInstanceValue();
138 if (LOGGER.isDebugEnabled()) {
139 LOGGER.atDebug().log("Removing resource '{}'", resource.getUuid());
140 }
141 backMatter.removeResource(resource);
142 index.removeItem(entity);
143 }
144 }
145
146 @Override
147 public DefaultResult visitGroup(
148 IAssemblyNodeItem item,
149 DefaultResult childResult,
150 Context context) {
151 CatalogGroup group = ObjectUtils.requireNonNull((CatalogGroup) item.getValue());
152
153 IIndexer index = context.getIndexer();
154 String groupId = group.getId();
155 DefaultResult retval = new DefaultResult();
156 if (SelectionStatus.SELECTED.equals(index.getSelectionStatus(item))) {
157 if (groupId != null) {
158
159 IEntityItem entity = ObjectUtils.requireNonNull(index.getEntity(ItemType.GROUP, groupId, false));
160
161 group.setId(entity.getIdentifier());
162 }
163 childResult.applyTo(group);
164 } else {
165 retval.removeGroup(group);
166 retval.appendPromoted(ObjectUtils.notNull(childResult));
167
168 if (groupId != null) {
169
170 IEntityItem entity = ObjectUtils.requireNonNull(index.getEntity(ItemType.GROUP, groupId, false));
171 index.removeItem(entity);
172 }
173
174
175 removePartsFromIndex(item, index);
176 }
177 return retval;
178 }
179
180 @Override
181 public DefaultResult visitControl(
182 IAssemblyNodeItem item,
183 DefaultResult childResult,
184 Context context) {
185 Control control = ObjectUtils.requireNonNull((Control) item.getValue());
186 IIndexer index = context.getIndexer();
187
188 IEntityItem entity = ObjectUtils.requireNonNull(
189 index.getEntity(ItemType.CONTROL, ObjectUtils.requireNonNull(control.getId()), false));
190
191 IAssemblyNodeItem parent = ObjectUtils.notNull(item.getParentContentNodeItem());
192 DefaultResult retval = new DefaultResult();
193 if (SelectionStatus.SELECTED.equals(index.getSelectionStatus(item))) {
194
195
196 control.setId(entity.getIdentifier());
197
198 if (!SelectionStatus.SELECTED.equals(index.getSelectionStatus(parent))) {
199
200 retval.promoteControl(control);
201 }
202 childResult.applyTo(control);
203 } else {
204
205
206 if (SelectionStatus.SELECTED.equals(index.getSelectionStatus(parent))) {
207 retval.removeControl(control);
208 }
209 retval.appendPromoted(ObjectUtils.notNull(childResult));
210 index.removeItem(entity);
211
212
213 removePartsFromIndex(item, index);
214 }
215 return retval;
216 }
217
218 protected static void removePartsFromIndex(@NonNull IAssemblyNodeItem groupOrControlItem,
219 @NonNull IIndexer index) {
220 CHILD_PART_METAPATH.evaluate(groupOrControlItem).stream()
221 .map(item -> (IAssemblyNodeItem) item)
222 .forEachOrdered(partItem -> {
223 ControlPart part = ObjectUtils.requireNonNull((ControlPart) partItem.getValue());
224 String id = part.getId();
225 if (id != null) {
226 IEntityItem entity = index.getEntity(IEntityItem.ItemType.PART, id);
227 if (entity != null) {
228 index.removeItem(entity);
229 }
230 }
231 });
232 }
233
234 @Override
235 protected DefaultResult visitParameter(IAssemblyNodeItem item, IAssemblyNodeItem parent,
236 Context context) {
237 Parameter param = ObjectUtils.requireNonNull((Parameter) item.getValue());
238 IIndexer index = context.getIndexer();
239
240 IEntityItem entity = ObjectUtils.requireNonNull(
241 index.getEntity(ItemType.PARAMETER, ObjectUtils.requireNonNull(param.getId()), false));
242
243 DefaultResult retval = new DefaultResult();
244 if (IIndexer.isReferencedEntity(entity)) {
245
246
247 param.setId(entity.getIdentifier());
248
249
250 index.setSelectionStatus(item, SelectionStatus.SELECTED);
251
252
253 if (SelectionStatus.UNSELECTED.equals(index.getSelectionStatus(parent))) {
254 retval.promoteParameter(param);
255 }
256 } else {
257
258 if (SelectionStatus.SELECTED.equals(index.getSelectionStatus(parent))) {
259 retval.removeParameter(param);
260 }
261 index.removeItem(entity);
262 }
263 return retval;
264 }
265
266 protected static final class Context {
267
268 @NonNull
269 private final IIndexer indexer;
270
271 private Context(@NonNull IIndexer indexer) {
272 this.indexer = indexer;
273 }
274
275 @NonNull
276 @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "provides intentional access to index state")
277 public IIndexer getIndexer() {
278 return indexer;
279 }
280 }
281 }