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.metaschema.core.metapath; 028 029import gov.nist.secauto.metaschema.core.configuration.DefaultConfiguration; 030import gov.nist.secauto.metaschema.core.configuration.IConfiguration; 031import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration; 032import gov.nist.secauto.metaschema.core.metapath.function.DefaultFunction.CallingContext; 033import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; 034import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; 035import gov.nist.secauto.metaschema.core.model.IUriResolver; 036import gov.nist.secauto.metaschema.core.util.ObjectUtils; 037 038import java.io.IOException; 039import java.io.InputStream; 040import java.net.URI; 041import java.net.URISyntaxException; 042import java.net.URL; 043import java.nio.file.Path; 044import java.time.Clock; 045import java.time.ZoneId; 046import java.time.ZonedDateTime; 047import java.util.Collections; 048import java.util.HashMap; 049import java.util.Map; 050import java.util.concurrent.ConcurrentHashMap; 051 052import edu.umd.cs.findbugs.annotations.NonNull; 053 054public class DynamicContext { // NOPMD - intentional data class 055 @NonNull 056 private final StaticContext staticContext; 057 @NonNull 058 private final ZoneId implicitTimeZone; 059 @NonNull 060 private final ZonedDateTime currentDateTime; 061 @NonNull 062 private final Map<URI, IDocumentNodeItem> availableDocuments; 063 private final Map<CallingContext, ISequence<?>> functionResultCache; 064 private CachingLoader documentLoader; 065 @NonNull 066 private final IMutableConfiguration<MetapathEvaluationFeature<?>> configuration; 067 @NonNull 068 private final Map<String, ISequence<?>> letVariableMap; 069 070 @SuppressWarnings("null") 071 public DynamicContext(@NonNull StaticContext staticContext) { 072 this.staticContext = staticContext; 073 074 Clock clock = Clock.systemDefaultZone(); 075 076 this.implicitTimeZone = clock.getZone(); 077 this.currentDateTime = ZonedDateTime.now(clock); 078 this.availableDocuments = new HashMap<>(); 079 this.functionResultCache = new HashMap<>(); 080 this.configuration = new DefaultConfiguration<>(); 081 this.configuration.enableFeature(MetapathEvaluationFeature.METAPATH_EVALUATE_PREDICATES); 082 this.letVariableMap = new ConcurrentHashMap<>(); 083 } 084 085 @NonNull 086 public StaticContext getStaticContext() { 087 return staticContext; 088 } 089 090 @NonNull 091 public ZoneId getImplicitTimeZone() { 092 return implicitTimeZone; 093 } 094 095 @NonNull 096 public ZonedDateTime getCurrentDateTime() { 097 return currentDateTime; 098 } 099 100 @SuppressWarnings("null") 101 @NonNull 102 public Map<URI, INodeItem> getAvailableDocuments() { 103 return Collections.unmodifiableMap(availableDocuments); 104 } 105 106 public IDocumentLoader getDocumentLoader() { 107 return documentLoader; 108 } 109 110 public void setDocumentLoader(@NonNull IDocumentLoader documentLoader) { 111 this.documentLoader = new CachingLoader(documentLoader); 112 } 113 114 public ISequence<?> getCachedResult(@NonNull CallingContext callingContext) { 115 return functionResultCache.get(callingContext); 116 } 117 118 @NonNull 119 public DynamicContext disablePredicateEvaluation() { 120 this.configuration.disableFeature(MetapathEvaluationFeature.METAPATH_EVALUATE_PREDICATES); 121 return this; 122 } 123 124 @NonNull 125 public IConfiguration<MetapathEvaluationFeature<?>> getConfiguration() { 126 return configuration; 127 } 128 129 public void cacheResult(@NonNull CallingContext callingContext, @NonNull ISequence<?> result) { 130 ISequence<?> old = functionResultCache.put(callingContext, result); 131 assert old == null; 132 } 133 134 @NonNull 135 public ISequence<?> getVariableValue(String name) { 136 return ObjectUtils.requireNonNull(letVariableMap.get(name)); 137 } 138 139 public void setVariableValue(String name, ISequence<?> boundValue) { 140 letVariableMap.put(name, boundValue); 141 } 142 143 public void clearVariableValue(String name) { 144 letVariableMap.remove(name); 145 } 146 147 private class CachingLoader implements IDocumentLoader { 148 @NonNull 149 private final IDocumentLoader proxy; 150 151 public CachingLoader(@NonNull IDocumentLoader proxy) { 152 this.proxy = proxy; 153 } 154 155 @Override 156 public IUriResolver getUriResolver() { 157 return new ContextUriResolver(); 158 } 159 160 @Override 161 public void setUriResolver(@NonNull IUriResolver resolver) { 162 // we delegate to the document loader proxy, so the resolver should be set there 163 throw new UnsupportedOperationException("Set the resolver on the proxy"); 164 } 165 166 @NonNull 167 protected IDocumentLoader getProxiedDocumentLoader() { 168 return proxy; 169 } 170 171 @Override 172 public IDocumentNodeItem loadAsNodeItem(Path path) throws IOException { 173 URI uri = path.toUri(); 174 IDocumentNodeItem retval = availableDocuments.get(uri); 175 if (retval == null) { 176 retval = getProxiedDocumentLoader().loadAsNodeItem(path); 177 availableDocuments.put(uri, retval); 178 } 179 return retval; 180 } 181 182 @Override 183 public IDocumentNodeItem loadAsNodeItem(URL url) throws IOException, URISyntaxException { 184 URI uri = ObjectUtils.notNull(url.toURI()); 185 IDocumentNodeItem retval = availableDocuments.get(uri); 186 if (retval == null) { 187 retval = getProxiedDocumentLoader().loadAsNodeItem(uri); 188 availableDocuments.put(uri, retval); 189 } 190 return retval; 191 } 192 193 @Override 194 public IDocumentNodeItem loadAsNodeItem(URI uri) throws IOException { 195 IDocumentNodeItem retval = availableDocuments.get(uri); 196 if (retval == null) { 197 retval = getProxiedDocumentLoader().loadAsNodeItem(uri); 198 availableDocuments.put(uri, retval); 199 } 200 return retval; 201 } 202 203 @Override 204 public @NonNull IDocumentNodeItem loadAsNodeItem( 205 @NonNull InputStream is, 206 @NonNull URI documentUri) throws IOException { 207 throw new UnsupportedOperationException(); 208 // return getProxiedDocumentLoader().loadAsNodeItem(is, documentUri); 209 } 210 211 public class ContextUriResolver implements IUriResolver { 212 213 /** 214 * {@inheritDoc} 215 * <p> 216 * This method first resolves the provided URI against the static context's base 217 * URI. 218 */ 219 @Override 220 public URI resolve(URI uri) { 221 URI baseUri = getStaticContext().getBaseUri(); 222 223 URI resolvedUri; 224 if (baseUri == null) { 225 resolvedUri = uri; 226 } else { 227 resolvedUri = ObjectUtils.notNull(baseUri.resolve(uri)); 228 } 229 230 IUriResolver resolver = getProxiedDocumentLoader().getUriResolver(); 231 return resolver == null ? resolvedUri : resolver.resolve(resolvedUri); 232 } 233 } 234 } 235}