001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package dev.metaschema.oscal.lib.model.util; 007 008import java.util.Collection; 009import java.util.LinkedHashMap; 010import java.util.LinkedList; 011import java.util.List; 012import java.util.Map; 013 014import dev.metaschema.core.metapath.DynamicContext; 015import dev.metaschema.core.metapath.StaticContext; 016import dev.metaschema.core.metapath.item.ISequence; 017import dev.metaschema.core.metapath.item.node.AbstractRecursionPreventingNodeItemVisitor; 018import dev.metaschema.core.metapath.item.node.IAssemblyInstanceGroupedNodeItem; 019import dev.metaschema.core.metapath.item.node.IAssemblyNodeItem; 020import dev.metaschema.core.metapath.item.node.IDefinitionNodeItem; 021import dev.metaschema.core.metapath.item.node.IFieldNodeItem; 022import dev.metaschema.core.metapath.item.node.IFlagNodeItem; 023import dev.metaschema.core.metapath.item.node.IModuleNodeItem; 024import dev.metaschema.core.metapath.item.node.INodeItemFactory; 025import dev.metaschema.core.model.IModule; 026import dev.metaschema.core.model.constraint.IAllowedValuesConstraint; 027import dev.metaschema.core.model.constraint.ILet; 028import edu.umd.cs.findbugs.annotations.NonNull; 029 030public class AllowedValueCollectingNodeItemVisitor 031 extends AbstractRecursionPreventingNodeItemVisitor<DynamicContext, Void> { 032 033 private final Map<IDefinitionNodeItem<?, ?>, NodeItemRecord> nodeItemAnalysis = new LinkedHashMap<>(); 034 035 public Collection<NodeItemRecord> getAllowedValueLocations() { 036 return nodeItemAnalysis.values(); 037 } 038 039 public void visit(@NonNull IModule module) { 040 DynamicContext context = new DynamicContext( 041 StaticContext.builder() 042 .defaultModelNamespace(module.getXmlNamespace()) 043 .build()); 044 context.disablePredicateEvaluation(); 045 046 visit(INodeItemFactory.instance().newModuleNodeItem(module), context); 047 } 048 049 public void visit(@NonNull IModuleNodeItem module, @NonNull DynamicContext context) { 050 051 visitMetaschema(module, context); 052 } 053 054 private void handleAllowedValuesAtLocation( 055 @NonNull IDefinitionNodeItem<?, ?> itemLocation, 056 @NonNull DynamicContext context) { 057 itemLocation.getDefinition().getAllowedValuesConstraints().stream() 058 .forEachOrdered(allowedValues -> { 059 ISequence<?> result = allowedValues.getTarget().evaluate(itemLocation, context); 060 result.stream().forEachOrdered(target -> { 061 assert target != null; 062 handleAllowedValues(allowedValues, itemLocation, (IDefinitionNodeItem<?, ?>) target); 063 }); 064 }); 065 } 066 067 private void handleAllowedValues( 068 @NonNull IAllowedValuesConstraint allowedValues, 069 @NonNull IDefinitionNodeItem<?, ?> location, 070 @NonNull IDefinitionNodeItem<?, ?> target) { 071 NodeItemRecord itemRecord = nodeItemAnalysis.get(target); 072 if (itemRecord == null) { 073 itemRecord = new NodeItemRecord(target); 074 nodeItemAnalysis.put(target, itemRecord); 075 } 076 077 AllowedValuesRecord allowedValuesRecord = new AllowedValuesRecord(allowedValues, location, target); 078 itemRecord.addAllowedValues(allowedValuesRecord); 079 } 080 081 @Override 082 public Void visitFlag(IFlagNodeItem item, DynamicContext context) { 083 assert context != null; 084 DynamicContext subContext = handleLetStatements(item, context); 085 handleAllowedValuesAtLocation(item, subContext); 086 return super.visitFlag(item, subContext); 087 } 088 089 @Override 090 public Void visitField(IFieldNodeItem item, DynamicContext context) { 091 assert context != null; 092 DynamicContext subContext = handleLetStatements(item, context); 093 handleAllowedValuesAtLocation(item, subContext); 094 return super.visitField(item, subContext); 095 } 096 097 @Override 098 public Void visitAssembly(IAssemblyNodeItem item, DynamicContext context) { 099 assert context != null; 100 DynamicContext subContext = handleLetStatements(item, context); 101 handleAllowedValuesAtLocation(item, subContext); 102 return super.visitAssembly(item, subContext); 103 } 104 105 private DynamicContext handleLetStatements(IDefinitionNodeItem<?, ?> item, DynamicContext context) { 106 assert context != null; 107 DynamicContext subContext = context; 108 for (ILet let : item.getDefinition().getLetExpressions().values()) { 109 ISequence<?> result = let.getValueExpression().evaluate(item, 110 subContext).reusable(); 111 subContext = subContext.bindVariableValue(let.getName(), result); 112 } 113 return subContext; 114 } 115 116 @Override 117 public Void visitAssembly(IAssemblyInstanceGroupedNodeItem item, DynamicContext context) { 118 return visitAssembly((IAssemblyNodeItem) item, context); 119 } 120 121 @Override 122 protected Void defaultResult() { 123 return null; 124 } 125 126 public static final class NodeItemRecord { 127 @NonNull 128 private final IDefinitionNodeItem<?, ?> item; 129 @NonNull 130 private final List<AllowedValuesRecord> allowedValues = new LinkedList<>(); 131 132 private NodeItemRecord(@NonNull IDefinitionNodeItem<?, ?> item) { 133 this.item = item; 134 } 135 136 @NonNull 137 public IDefinitionNodeItem<?, ?> getItem() { 138 return item; 139 } 140 141 @NonNull 142 public List<AllowedValuesRecord> getAllowedValues() { 143 return allowedValues; 144 } 145 146 public void addAllowedValues(@NonNull AllowedValuesRecord record) { 147 this.allowedValues.add(record); 148 } 149 } 150 151 public static final class AllowedValuesRecord { 152 @NonNull 153 private final IAllowedValuesConstraint allowedValues; 154 @NonNull 155 private final IDefinitionNodeItem<?, ?> location; 156 @NonNull 157 private final IDefinitionNodeItem<?, ?> target; 158 159 public AllowedValuesRecord( 160 @NonNull IAllowedValuesConstraint allowedValues, 161 @NonNull IDefinitionNodeItem<?, ?> location, 162 @NonNull IDefinitionNodeItem<?, ?> target) { 163 this.allowedValues = allowedValues; 164 this.location = location; 165 this.target = target; 166 } 167 168 @NonNull 169 public IAllowedValuesConstraint getAllowedValues() { 170 return allowedValues; 171 } 172 173 @NonNull 174 public IDefinitionNodeItem<?, ?> getLocation() { 175 return location; 176 } 177 178 @NonNull 179 public IDefinitionNodeItem<?, ?> getTarget() { 180 return target; 181 } 182 } 183}