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