001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package gov.nist.secauto.oscal.lib.profile.resolver.policy; 007 008import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem; 009import gov.nist.secauto.metaschema.core.util.ObjectUtils; 010import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionEvaluationException; 011import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem; 012 013import org.apache.logging.log4j.LogManager; 014import org.apache.logging.log4j.Logger; 015 016import java.util.List; 017 018import edu.umd.cs.findbugs.annotations.NonNull; 019import edu.umd.cs.findbugs.annotations.Nullable; 020 021public abstract class AbstractCustomReferencePolicy<TYPE> implements ICustomReferencePolicy<TYPE> { 022 private static final Logger LOGGER = LogManager.getLogger(AbstractCustomReferencePolicy.class); 023 024 @NonNull 025 private final IIdentifierParser identifierParser; 026 027 protected AbstractCustomReferencePolicy( 028 @NonNull IIdentifierParser identifierParser) { 029 this.identifierParser = identifierParser; 030 } 031 032 @Override 033 @NonNull 034 public IIdentifierParser getIdentifierParser() { 035 return identifierParser; 036 } 037 038 /** 039 * Get the possible item types that can be searched in the order in which the 040 * identifier will be looked up. 041 * <p> 042 * The {@code reference} object is provided to allow for context sensitive item 043 * type tailoring. 044 * 045 * @param reference 046 * the reference object 047 * @return a list of item types to search for 048 */ 049 @NonNull 050 protected abstract List<IEntityItem.ItemType> getEntityItemTypes(@NonNull TYPE reference); 051 052 /** 053 * Handle an index hit. 054 * 055 * @param contextItem 056 * the node containing the identifier reference 057 * @param reference 058 * the identifier reference object generating the hit 059 * @param item 060 * the referenced item 061 * @param visitorContext 062 * the reference visitor state, which can be used for further 063 * processing 064 * @return {@code true} if the hit was handled or {@code false} otherwise 065 * @throws ProfileResolutionEvaluationException 066 * if there was an error handing the index hit 067 */ 068 protected boolean handleIndexHit( 069 @NonNull IModelNodeItem<?, ?> contextItem, 070 @NonNull TYPE reference, 071 @NonNull IEntityItem item, 072 @NonNull ReferenceCountingVisitor.Context visitorContext) { 073 074 if (visitorContext.getIndexer().isSelected(item)) { 075 if (!visitorContext.isResolved(item)) { 076 // this referenced item will need to be resolved 077 ReferenceCountingVisitor.instance().resolveEntity(item, visitorContext); 078 } 079 item.incrementReferenceCount(); 080 081 if (item.isIdentifierReassigned()) { 082 String referenceText = ObjectUtils.notNull(getReferenceText(reference)); 083 String newReferenceText = getIdentifierParser().update(referenceText, item.getIdentifier()); 084 setReferenceText(reference, newReferenceText); 085 if (LOGGER.isDebugEnabled()) { 086 LOGGER.atDebug().log("Mapping {} reference '{}' to '{}'.", item.getItemType().name(), referenceText, 087 newReferenceText); 088 } 089 } 090 handleSelected(contextItem, reference, item, visitorContext); 091 } else { 092 handleUnselected(contextItem, reference, item, visitorContext); 093 } 094 return true; 095 } 096 097 /** 098 * Handle an index hit against an item related to an unselected control. 099 * <p> 100 * Subclasses can override this method to perform extra processing. 101 * 102 * @param contextItem 103 * the node containing the identifier reference 104 * @param reference 105 * the identifier reference object generating the hit 106 * @param item 107 * the referenced item 108 * @param visitorContext 109 * the reference visitor, which can be used for further processing 110 * @throws ProfileResolutionEvaluationException 111 * if there was an error handing the index hit 112 */ 113 protected void handleUnselected( // NOPMD noop default 114 @NonNull IModelNodeItem<?, ?> contextItem, 115 @NonNull TYPE reference, 116 @NonNull IEntityItem item, 117 @NonNull ReferenceCountingVisitor.Context visitorContext) { 118 // do nothing by default 119 } 120 121 /** 122 * Handle an index hit against an item related to an selected control. 123 * <p> 124 * Subclasses can override this method to perform extra processing. 125 * 126 * @param contextItem 127 * the node containing the identifier reference 128 * @param reference 129 * the identifier reference object generating the hit 130 * @param item 131 * the referenced item 132 * @param visitorContext 133 * the reference visitor state, which can be used for further 134 * processing 135 * @throws ProfileResolutionEvaluationException 136 * if there was an error handing the index hit 137 */ 138 protected void handleSelected( // NOPMD noop default 139 @NonNull IModelNodeItem<?, ?> contextItem, 140 @NonNull TYPE reference, 141 @NonNull IEntityItem item, 142 @NonNull ReferenceCountingVisitor.Context visitorContext) { 143 // do nothing by default 144 } 145 146 /** 147 * Handle an index miss for a reference. This occurs when the referenced item 148 * was not found in the index. 149 * <p> 150 * Subclasses can override this method to perform extra processing. 151 * 152 * @param contextItem 153 * the node containing the identifier reference 154 * @param reference 155 * the identifier reference object generating the hit 156 * @param itemTypes 157 * the possible item types for this reference 158 * @param identifier 159 * the parsed identifier 160 * @param visitorContext 161 * the reference visitor state, which can be used for further 162 * processing 163 * @return {@code true} if the reference is handled by this method or 164 * {@code false} otherwise 165 * @throws ProfileResolutionEvaluationException 166 * if there was an error handing the index miss 167 */ 168 protected boolean handleIndexMiss( 169 @NonNull IModelNodeItem<?, ?> contextItem, 170 @NonNull TYPE reference, 171 @NonNull List<IEntityItem.ItemType> itemTypes, 172 @NonNull String identifier, 173 @NonNull ReferenceCountingVisitor.Context visitorContext) { 174 // provide no handler by default 175 return false; 176 } 177 178 /** 179 * Handle the case where the identifier was not a syntax match for an expected 180 * identifier. This can occur when the reference is malformed, using an 181 * unrecognized syntax. 182 * <p> 183 * Subclasses can override this method to perform extra processing. 184 * 185 * @param contextItem 186 * the node containing the identifier reference 187 * @param reference 188 * the identifier reference object generating the hit 189 * @param visitorContext 190 * the reference visitor state, which can be used for further 191 * processing 192 * @return {@code true} if the reference is handled by this method or 193 * {@code false} otherwise 194 * @throws ProfileResolutionEvaluationException 195 * if there was an error handing the index miss due to a non match 196 */ 197 protected boolean handleIdentifierNonMatch( 198 @NonNull IModelNodeItem<?, ?> contextItem, 199 @NonNull TYPE reference, 200 @NonNull ReferenceCountingVisitor.Context visitorContext) { 201 // provide no handler by default 202 return false; 203 } 204 205 @Override 206 public boolean handleReference( 207 @NonNull IModelNodeItem<?, ?> contextItem, 208 @NonNull TYPE type, 209 @NonNull ReferenceCountingVisitor.Context visitorContext) { 210 String referenceText = getReferenceText(type); 211 212 // if the reference text does not exist, ignore the reference; otherwise, handle 213 // it. 214 return referenceText == null 215 || handleIdentifier(contextItem, type, getIdentifierParser().parse(referenceText), visitorContext); 216 } 217 218 /** 219 * Handle the provided {@code identifier} for a given {@code type} of reference. 220 * 221 * @param contextItem 222 * the node containing the identifier reference 223 * @param type 224 * the item type of the reference 225 * @param identifier 226 * the identifier 227 * @param visitorContext 228 * the reference visitor state, which can be used for further 229 * processing 230 * @return {@code true} if the reference is handled by this method or 231 * {@code false} otherwise 232 * @throws ProfileResolutionEvaluationException 233 * if there was an error handing the reference 234 */ 235 protected boolean handleIdentifier( 236 @NonNull IModelNodeItem<?, ?> contextItem, 237 @NonNull TYPE type, 238 @Nullable String identifier, 239 @NonNull ReferenceCountingVisitor.Context visitorContext) { 240 boolean retval; 241 if (identifier == null) { 242 retval = handleIdentifierNonMatch(contextItem, type, visitorContext); 243 } else { 244 List<IEntityItem.ItemType> itemTypes = getEntityItemTypes(type); 245 IEntityItem item = null; 246 for (IEntityItem.ItemType itemType : itemTypes) { 247 assert itemType != null; 248 249 item = visitorContext.getEntity(itemType, identifier); 250 if (item != null) { 251 break; 252 } 253 } 254 255 if (item == null) { 256 retval = handleIndexMiss(contextItem, type, itemTypes, identifier, visitorContext); 257 } else { 258 retval = handleIndexHit(contextItem, type, item, visitorContext); 259 } 260 } 261 return retval; 262 } 263}