1
2
3
4
5
6 package dev.metaschema.oscal.lib.profile.resolver.selection;
7
8 import com.fasterxml.jackson.core.Version;
9 import com.fasterxml.jackson.core.util.VersionUtil;
10
11 import java.net.URI;
12 import java.util.LinkedList;
13 import java.util.List;
14 import java.util.stream.Collectors;
15
16 import dev.metaschema.core.metapath.item.node.IAssemblyNodeItem;
17 import dev.metaschema.core.metapath.item.node.IDocumentNodeItem;
18 import dev.metaschema.core.metapath.item.node.INodeItem;
19 import dev.metaschema.core.metapath.item.node.IRootAssemblyNodeItem;
20 import dev.metaschema.core.util.CollectionUtil;
21 import dev.metaschema.core.util.ObjectUtils;
22 import dev.metaschema.oscal.lib.model.BackMatter;
23 import dev.metaschema.oscal.lib.model.BackMatter.Resource;
24 import dev.metaschema.oscal.lib.model.Catalog;
25 import dev.metaschema.oscal.lib.model.CatalogGroup;
26 import dev.metaschema.oscal.lib.model.Control;
27 import dev.metaschema.oscal.lib.model.Metadata;
28 import dev.metaschema.oscal.lib.model.Metadata.Location;
29 import dev.metaschema.oscal.lib.model.Metadata.Party;
30 import dev.metaschema.oscal.lib.model.Metadata.Role;
31 import dev.metaschema.oscal.lib.model.Parameter;
32 import dev.metaschema.oscal.lib.model.ProfileImport;
33 import dev.metaschema.oscal.lib.profile.resolver.ProfileResolutionEvaluationException;
34 import dev.metaschema.oscal.lib.profile.resolver.ProfileResolutionException;
35 import dev.metaschema.oscal.lib.profile.resolver.ProfileResolver.UriResolver;
36 import dev.metaschema.oscal.lib.profile.resolver.policy.ReferenceCountingVisitor;
37 import dev.metaschema.oscal.lib.profile.resolver.support.BasicIndexer;
38 import dev.metaschema.oscal.lib.profile.resolver.support.IEntityItem;
39 import dev.metaschema.oscal.lib.profile.resolver.support.IIndexer;
40 import dev.metaschema.oscal.lib.profile.resolver.support.IIndexer.SelectionStatus;
41 import edu.umd.cs.findbugs.annotations.NonNull;
42
43 public class Import {
44
45 @NonNull
46 private final IRootAssemblyNodeItem profile;
47 @NonNull
48 private final IAssemblyNodeItem profileImportItem;
49
50 public Import(
51 @NonNull IRootAssemblyNodeItem profile,
52 @NonNull IAssemblyNodeItem profileImportItem) {
53
54 this.profile = profile;
55 this.profileImportItem = profileImportItem;
56 }
57
58 protected IRootAssemblyNodeItem getProfileItem() {
59 return profile;
60 }
61
62 protected IAssemblyNodeItem getProfileImportItem() {
63 return profileImportItem;
64 }
65
66 @NonNull
67 protected ProfileImport getProfileImport() {
68 return ObjectUtils.requireNonNull((ProfileImport) profileImportItem.getValue());
69 }
70
71 private static Catalog toCatalog(@NonNull IDocumentNodeItem catalogDocument) {
72 return (Catalog) INodeItem.toValue(catalogDocument);
73 }
74
75 @NonNull
76 protected IControlFilter newControlFilter() {
77 return IControlFilter.newInstance(getProfileImport());
78 }
79
80 @NonNull
81 protected IIndexer newIndexer() {
82
83
84
85 return new BasicIndexer();
86 }
87
88 @NonNull
89 public IIndexer resolve(
90 @NonNull IDocumentNodeItem importedCatalogDocument,
91 @NonNull Catalog resolvedCatalog,
92 @NonNull UriResolver uriResolver)
93 throws ProfileResolutionException {
94 ProfileImport profileImport = getProfileImport();
95 URI uri = ObjectUtils.requireNonNull(profileImport.getHref(), "profile import href is null");
96
97
98 IControlFilter filter = newControlFilter();
99 IIndexer indexer = newIndexer();
100
101
102 indexer.setSelectionStatus(importedCatalogDocument.getRootAssemblyNodeItem(), SelectionStatus.SELECTED);
103 IControlSelectionState state = new ControlSelectionState(indexer, filter);
104
105 try {
106 ControlSelectionVisitor.instance().visitCatalog(importedCatalogDocument, state);
107
108
109 ReferenceCountingVisitor.instance().visitCatalog(importedCatalogDocument, indexer, uriResolver);
110
111
112 FilterNonSelectedVisitor.instance().visitCatalog(importedCatalogDocument, indexer);
113 } catch (ProfileResolutionEvaluationException ex) {
114 throw new ProfileResolutionException(
115 String.format("Import: Unable to resolve profile import '%s'. %s", uri.toString(), ex.getMessage()), ex);
116 }
117
118 Catalog importedCatalog = toCatalog(importedCatalogDocument);
119 for (Parameter param : CollectionUtil.listOrEmpty(importedCatalog.getParams())) {
120 if (param != null) {
121 resolvedCatalog.addParam(param);
122 }
123 }
124 for (Control control : CollectionUtil.listOrEmpty(importedCatalog.getControls())) {
125 if (control != null) {
126 resolvedCatalog.addControl(control);
127 }
128 }
129 for (CatalogGroup group : CollectionUtil.listOrEmpty(importedCatalog.getGroups())) {
130 if (group != null) {
131 resolvedCatalog.addGroup(group);
132 }
133 }
134
135 generateMetadata(importedCatalogDocument, resolvedCatalog, indexer);
136 generateBackMatter(importedCatalogDocument, resolvedCatalog, indexer);
137 return indexer;
138 }
139
140 private static void generateMetadata(
141 @NonNull IDocumentNodeItem importedCatalogDocument,
142 @NonNull Catalog resolvedCatalog,
143 @NonNull IIndexer indexer) {
144 Metadata importedMetadata = toCatalog(importedCatalogDocument).getMetadata();
145
146 if (importedMetadata != null) {
147 resolveMetadata(importedMetadata, resolvedCatalog.getMetadata(), indexer);
148 }
149 }
150
151 private static void resolveMetadata(
152 @NonNull Metadata imported,
153 @NonNull Metadata resolved,
154 @NonNull IIndexer indexer) {
155 String importedVersion = imported.getOscalVersion();
156 if (importedVersion != null) {
157 Version importOscalVersion = VersionUtil.parseVersion(importedVersion, null, null);
158
159 Version resolvedCatalogVersion
160 = VersionUtil.parseVersion(resolved.getOscalVersion(), null, null);
161
162 if (importOscalVersion.compareTo(resolvedCatalogVersion) > 0) {
163 resolved.setOscalVersion(importOscalVersion.toString());
164 }
165 }
166
167
168 resolved.setRoles(
169 IIndexer.filterDistinct(
170 ObjectUtils.notNull(CollectionUtil.listOrEmpty(resolved.getRoles()).stream()),
171 indexer.getEntitiesByItemType(IEntityItem.ItemType.ROLE),
172 Role::getId)
173 .collect(Collectors.toCollection(LinkedList::new)));
174 resolved.setParties(
175 IIndexer.filterDistinct(
176 ObjectUtils.notNull(CollectionUtil.listOrEmpty(resolved.getParties()).stream()),
177 indexer.getEntitiesByItemType(IEntityItem.ItemType.PARTY),
178 Party::getUuid)
179 .collect(Collectors.toCollection(LinkedList::new)));
180 resolved.setLocations(
181 IIndexer.filterDistinct(
182 ObjectUtils.notNull(CollectionUtil.listOrEmpty(resolved.getLocations()).stream()),
183 indexer.getEntitiesByItemType(IEntityItem.ItemType.LOCATION),
184 Location::getUuid)
185 .collect(Collectors.toCollection(LinkedList::new)));
186 }
187
188 @SuppressWarnings("PMD.AvoidDeeplyNestedIfStmts")
189 private static void generateBackMatter(
190 @NonNull IDocumentNodeItem importedCatalogDocument,
191 @NonNull Catalog resolvedCatalog,
192 @NonNull IIndexer indexer) {
193 BackMatter importedBackMatter = toCatalog(importedCatalogDocument).getBackMatter();
194
195 if (importedBackMatter != null) {
196 BackMatter resolvedBackMatter = resolvedCatalog.getBackMatter();
197
198 List<Resource> resolvedResources = resolvedBackMatter == null ? CollectionUtil.emptyList()
199 : CollectionUtil.listOrEmpty(resolvedBackMatter.getResources());
200
201 List<Resource> resources = IIndexer.filterDistinct(
202 ObjectUtils.notNull(resolvedResources.stream()),
203 indexer.getEntitiesByItemType(IEntityItem.ItemType.RESOURCE),
204 Resource::getUuid)
205 .collect(Collectors.toCollection(LinkedList::new));
206
207 if (!resources.isEmpty()) {
208 if (resolvedBackMatter == null) {
209 resolvedBackMatter = new BackMatter();
210 resolvedCatalog.setBackMatter(resolvedBackMatter);
211 }
212
213 resolvedBackMatter.setResources(resources);
214 }
215 }
216 }
217 }