001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.oscal.lib.profile.resolver.policy; 028 029import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem; 030import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; 031import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionEvaluationException; 032import gov.nist.secauto.oscal.lib.profile.resolver.support.IEntityItem; 033 034import org.apache.logging.log4j.LogManager; 035import org.apache.logging.log4j.Logger; 036 037import java.util.List; 038 039import edu.umd.cs.findbugs.annotations.NonNull; 040import edu.umd.cs.findbugs.annotations.Nullable; 041 042public abstract class AbstractCustomReferencePolicy<TYPE> implements ICustomReferencePolicy<TYPE> { 043 private static final Logger LOGGER = LogManager.getLogger(AbstractCustomReferencePolicy.class); 044 045 @NonNull 046 private final IIdentifierParser identifierParser; 047 048 protected AbstractCustomReferencePolicy( 049 @NonNull IIdentifierParser identifierParser) { 050 this.identifierParser = identifierParser; 051 } 052 053 @Override 054 @NonNull 055 public IIdentifierParser getIdentifierParser() { 056 return identifierParser; 057 } 058 059 /** 060 * Get the possible item types that can be searched in the order in which the 061 * identifier will be looked up. 062 * <p> 063 * The {@code reference} object is provided to allow for context sensitive item 064 * type tailoring. 065 * 066 * @param reference 067 * the reference object 068 * @return a list of item types to search for 069 */ 070 @NonNull 071 protected abstract List<IEntityItem.ItemType> getEntityItemTypes(@NonNull TYPE reference); 072 073 /** 074 * Handle an index hit. 075 * 076 * @param contextItem 077 * the node containing the identifier reference 078 * @param reference 079 * the identifier reference object generating the hit 080 * @param item 081 * the referenced item 082 * @param visitorContext 083 * the reference visitor state, which can be used for further 084 * processing 085 * @return {@code true} if the hit was handled or {@code false} otherwise 086 * @throws ProfileResolutionEvaluationException 087 * if there was an error handing the index hit 088 */ 089 protected boolean handleIndexHit( 090 @NonNull IRequiredValueModelNodeItem contextItem, 091 @NonNull TYPE reference, 092 @NonNull IEntityItem item, 093 @NonNull ReferenceCountingVisitor.Context visitorContext) { 094 095 if (visitorContext.getIndexer().isSelected(item)) { 096 if (!visitorContext.isResolved(item)) { 097 // this referenced item will need to be resolved 098 ReferenceCountingVisitor.instance().resolveEntity(item, visitorContext); 099 } 100 item.incrementReferenceCount(); 101 102 if (item.isIdentifierReassigned()) { 103 String referenceText = ObjectUtils.notNull(getReferenceText(reference)); 104 String newReferenceText = getIdentifierParser().update(referenceText, item.getIdentifier()); 105 setReferenceText(reference, newReferenceText); 106 if (LOGGER.isDebugEnabled()) { 107 LOGGER.atDebug().log("Mapping {} reference '{}' to '{}'.", item.getItemType().name(), referenceText, 108 newReferenceText); 109 } 110 } 111 handleSelected(contextItem, reference, item, visitorContext); 112 } else { 113 handleUnselected(contextItem, reference, item, visitorContext); 114 } 115 return true; 116 } 117 118 /** 119 * Handle an index hit against an item related to an unselected control. 120 * <p> 121 * Subclasses can override this method to perform extra processing. 122 * 123 * @param contextItem 124 * the node containing the identifier reference 125 * @param reference 126 * the identifier reference object generating the hit 127 * @param item 128 * the referenced item 129 * @param visitorContext 130 * the reference visitor, which can be used for further processing 131 * @throws ProfileResolutionEvaluationException 132 * if there was an error handing the index hit 133 */ 134 protected void handleUnselected( // NOPMD noop default 135 @NonNull IRequiredValueModelNodeItem contextItem, 136 @NonNull TYPE reference, 137 @NonNull IEntityItem item, 138 @NonNull ReferenceCountingVisitor.Context visitorContext) { 139 // do nothing by default 140 } 141 142 /** 143 * Handle an index hit against an item related to an selected control. 144 * <p> 145 * Subclasses can override this method to perform extra processing. 146 * 147 * @param contextItem 148 * the node containing the identifier reference 149 * @param reference 150 * the identifier reference object generating the hit 151 * @param item 152 * the referenced item 153 * @param visitorContext 154 * the reference visitor state, which can be used for further 155 * processing 156 * @throws ProfileResolutionEvaluationException 157 * if there was an error handing the index hit 158 */ 159 protected void handleSelected( // NOPMD noop default 160 @NonNull IRequiredValueModelNodeItem contextItem, 161 @NonNull TYPE reference, 162 @NonNull IEntityItem item, 163 @NonNull ReferenceCountingVisitor.Context visitorContext) { 164 // do nothing by default 165 } 166 167 /** 168 * Handle an index miss for a reference. This occurs when the referenced item 169 * was not found in the index. 170 * <p> 171 * Subclasses can override this method to perform extra processing. 172 * 173 * @param contextItem 174 * the node containing the identifier reference 175 * @param reference 176 * the identifier reference object generating the hit 177 * @param itemTypes 178 * the possible item types for this reference 179 * @param identifier 180 * the parsed identifier 181 * @param visitorContext 182 * the reference visitor state, which can be used for further 183 * processing 184 * @return {@code true} if the reference is handled by this method or 185 * {@code false} otherwise 186 * @throws ProfileResolutionEvaluationException 187 * if there was an error handing the index miss 188 */ 189 protected boolean handleIndexMiss( 190 @NonNull IRequiredValueModelNodeItem contextItem, 191 @NonNull TYPE reference, 192 @NonNull List<IEntityItem.ItemType> itemTypes, 193 @NonNull String identifier, 194 @NonNull ReferenceCountingVisitor.Context visitorContext) { 195 // provide no handler by default 196 return false; 197 } 198 199 /** 200 * Handle the case where the identifier was not a syntax match for an expected 201 * identifier. This can occur when the reference is malformed, using an 202 * unrecognized syntax. 203 * <p> 204 * Subclasses can override this method to perform extra processing. 205 * 206 * @param contextItem 207 * the node containing the identifier reference 208 * @param reference 209 * the identifier reference object generating the hit 210 * @param visitorContext 211 * the reference visitor state, which can be used for further 212 * processing 213 * @return {@code true} if the reference is handled by this method or 214 * {@code false} otherwise 215 * @throws ProfileResolutionEvaluationException 216 * if there was an error handing the index miss due to a non match 217 */ 218 protected boolean handleIdentifierNonMatch( 219 @NonNull IRequiredValueModelNodeItem contextItem, 220 @NonNull TYPE reference, 221 @NonNull ReferenceCountingVisitor.Context visitorContext) { 222 // provide no handler by default 223 return false; 224 } 225 226 @Override 227 public boolean handleReference( 228 @NonNull IRequiredValueModelNodeItem contextItem, 229 @NonNull TYPE type, 230 @NonNull ReferenceCountingVisitor.Context visitorContext) { 231 String referenceText = getReferenceText(type); 232 233 // if the reference text does not exist, ignore the reference; otherwise, handle 234 // it. 235 return referenceText == null 236 || handleIdentifier(contextItem, type, getIdentifierParser().parse(referenceText), visitorContext); 237 } 238 239 /** 240 * Handle the provided {@code identifier} for a given {@code type} of reference. 241 * 242 * @param contextItem 243 * the node containing the identifier reference 244 * @param type 245 * the item type of the reference 246 * @param identifier 247 * the identifier 248 * @param visitorContext 249 * the reference visitor state, which can be used for further 250 * processing 251 * @return {@code true} if the reference is handled by this method or 252 * {@code false} otherwise 253 * @throws ProfileResolutionEvaluationException 254 * if there was an error handing the reference 255 */ 256 protected boolean handleIdentifier( 257 @NonNull IRequiredValueModelNodeItem contextItem, 258 @NonNull TYPE type, 259 @Nullable String identifier, 260 @NonNull ReferenceCountingVisitor.Context visitorContext) { 261 boolean retval; 262 if (identifier == null) { 263 retval = handleIdentifierNonMatch(contextItem, type, visitorContext); 264 } else { 265 List<IEntityItem.ItemType> itemTypes = getEntityItemTypes(type); 266 IEntityItem item = null; 267 for (IEntityItem.ItemType itemType : itemTypes) { 268 assert itemType != null; 269 270 item = visitorContext.getEntity(itemType, identifier); 271 if (item != null) { 272 break; 273 } 274 } 275 276 if (item == null) { 277 retval = handleIndexMiss(contextItem, type, itemTypes, identifier, visitorContext); 278 } else { 279 retval = handleIndexHit(contextItem, type, item, visitorContext); 280 } 281 } 282 return retval; 283 } 284}