001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2022 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 014 * implied. See the License for the specific language governing 015 * permissions and limitations under the License. 016 */ 017package org.microbean.loader.spi; 018 019import java.lang.reflect.ParameterizedType; 020import java.lang.reflect.Type; 021 022import java.util.function.Supplier; 023 024import org.microbean.development.annotation.OverridingEncouraged; 025 026import org.microbean.loader.api.Loader; 027 028import org.microbean.path.Path; 029 030/** 031 * A skeletal {@link Provider} implementation. 032 * 033 * @author <a href="https://about.me/lairdnelson" 034 * target="_parent">Laird Nelson</a> 035 * 036 * @see #lowerBound() 037 * 038 * @see Provider 039 */ 040public abstract class AbstractProvider implements Provider { 041 042 043 /* 044 * Instance fields. 045 */ 046 047 048 private final Type lowerBound; 049 050 051 /* 052 * Constructors. 053 */ 054 055 056 /** 057 * Creates a new {@link AbstractProvider} with {@code null} as its 058 * {@linkplain #lowerBound() lower type bound}. 059 * 060 * @see #AbstractProvider(Type) 061 * 062 * @see #lowerBound() 063 */ 064 protected AbstractProvider() { 065 this(null); 066 } 067 068 /** 069 * Creates a new {@link AbstractProvider}. 070 * 071 * @param lowerBound the {@linkplain #lowerBound() lower type bound} 072 * of this {@link AbstractProvider}; may be, and often is, {@code 073 * null} 074 * 075 * @see #lowerBound() 076 */ 077 protected AbstractProvider(final Type lowerBound) { 078 super(); 079 this.lowerBound = lowerBound; 080 } 081 082 083 /* 084 * Instance methods. 085 */ 086 087 088 /** 089 * Returns a {@link Type} representing the <strong>lower bound of 090 * all possible {@linkplain Value values}</strong> {@linkplain 091 * #get(Loader, Path) supplied} by this {@link AbstractProvider}. 092 * 093 * @return the lower type bound of this {@link AbstractProvider} 094 * implementation, or {@code null} if its lower bound is the lowest 095 * possible bound 096 * 097 * @nullability This method may return {@code null}. 098 * 099 * @idempotency This method is idempotent and deterministic. 100 * 101 * @threadsafety This method is safe for concurrent use by multiple 102 * threads. 103 */ 104 @Override // Provider 105 public final Type lowerBound() { 106 return this.lowerBound; 107 } 108 109 /** 110 * Implements the {@link Provider#get(Loader, Path)} method to first 111 * call the {@link #find(Loader, Path)} method, and, if necessary, 112 * the {@link #path(Loader, Path)} method, and to then return a 113 * {@link Value} made up of the relevant return values. 114 * 115 * @param requestor the {@link Loader} seeking a {@link Value}; 116 * must not be {@code null} 117 * 118 * @param absolutePath an {@linkplain Path#absolute() absolute 119 * <code>Path</code>} for which the supplied {@link Loader} is 120 * seeking a value; must not be {@code null} 121 * 122 * @return a {@link Value} more or less suitable for the combination 123 * of the supplied {@link Loader} and {@link Path}, or {@code 124 * null} if there is no such {@link Value} now <strong>and if there 125 * never will be such a {@link Value}</strong> for the supplied 126 * arguments 127 * 128 * @exception NullPointerException if either {@code requestor} or 129 * {@code absolutePath} is {@code null} 130 * 131 * @exception IllegalArgumentException if {@code absolutePath} 132 * {@linkplain Path#absolute() is not absolute}, or if {@link 133 * Path#startsWith(Path) 134 * !absolutePath.startsWith(requestor.absolutePath())}, or if {@link 135 * Path#equals(Object) 136 * absolutePath.equals(requestor.absolutePath())} 137 * 138 * @nullability This method may return {@code null}. 139 * 140 * @idempotency This method is idempotent, but not necessarily 141 * deterministic. 142 * 143 * @threadsafety This method is safe for concurrent use by multiple 144 * threads. 145 * 146 * @see Provider#get(Loader, Path) 147 * 148 * @see #find(Loader, Path) 149 * 150 * @see #path(Loader, Path) 151 */ 152 @Override // Provider 153 public final Value<?> get(final Loader<?> requestor, final Path<? extends Type> absolutePath) { 154 final Supplier<?> s = this.find(requestor, absolutePath); 155 if (s != null) { 156 if (s instanceof Value<?> v) { 157 return v; 158 } 159 final Path<? extends Type> p = this.path(requestor, absolutePath); 160 if (p != null) { 161 return new Value<>(s, p); 162 } 163 } 164 return null; 165 } 166 167 /** 168 * Returns a {@link Supplier} suitable for the supplied {@link 169 * Loader} and {@link Path}, or {@code null} if there is no such 170 * {@link Supplier} now <strong>and if there never will be such a 171 * {@link Supplier}</strong> for the supplied arguments. 172 * 173 * <p>Overrides of this method must not call the {@link #get(Loader, 174 * Path)} method or undefined behavior (such as an infinite loop) 175 * may result.</p> 176 * 177 * @param requestor the {@link Loader} seeking a {@link Value} (as 178 * originally passed to the {@link #get(Loader, Path)} method); must 179 * not be {@code null} 180 * 181 * @param absolutePath an {@linkplain Path#absolute() absolute 182 * <code>Path</code>} for which the supplied {@link Loader} is 183 * seeking a value (as originally passed to the {@link #get(Loader, 184 * Path)} method); must not be {@code null} 185 * 186 * @return a {@link Supplier} more or less suitable for the 187 * combination of the supplied {@link Loader} and {@link Path}, or 188 * {@code null} if there is no such {@link Supplier} now <strong>and 189 * if there never will be such a {@link Supplier}</strong> for the 190 * supplied arguments 191 * 192 * @exception NullPointerException if either {@code requestor} or 193 * {@code absolutePath} is {@code null} 194 * 195 * @exception IllegalArgumentException if {@code absolutePath} 196 * {@linkplain Path#absolute() is not absolute}, or if {@link 197 * Path#startsWith(Path) 198 * !absolutePath.startsWith(requestor.absolutePath())}, or if {@link 199 * Path#equals(Object) 200 * absolutePath.equals(requestor.absolutePath())} 201 * 202 * @nullability Overrides of this method may return {@code null}, in 203 * which case the {@link #get(Loader, Path)} method will return 204 * {@code null} as well. 205 * 206 * @idempotency Overrides of this method must be idempotent but are 207 * not assumed to be deterministic. 208 * 209 * @threadsafety Overrides of this method must be safe for 210 * concurrent use by multiple threads. 211 * 212 * @see #get(Loader, Path) 213 */ 214 protected abstract Supplier<?> find(final Loader<?> requestor, final Path<? extends Type> absolutePath); 215 216 /** 217 * Returns a {@link Path} that will be {@linkplain 218 * Value#Value(Supplier, Path) used to build a <code>Value</code>} 219 * to be returned by the {@link #get(Loader, Path)} method. 220 * 221 * <p>This method does not, and its overrides must not, call the 222 * {@link #get(Loader, Path)} method or undefined behavior (such as 223 * an infinite loop) may result.</p> 224 * 225 * <p>The default implementation of this method returns the supplied 226 * {@code absolutePath}.</p> 227 * 228 * <p>This method is called by the {@link #get(Loader, Path)} method 229 * only when a call to the {@link #find(Loader, Path)} method 230 * returns a non-{@code null} value.</p> 231 * 232 * @param <T> the type of both the supplied and returned {@link 233 * Path}s 234 * 235 * @param requestor requestor the {@link Loader} seeking a {@link 236 * Value} (as originally passed to the {@link #get(Loader, Path)} 237 * method); must not be {@code null} 238 * 239 * @param absolutePath an {@linkplain Path#absolute() absolute 240 * <code>Path</code>} for which the supplied {@link Loader} is 241 * seeking a value (as originally passed to the {@link #get(Loader, 242 * Path)} method); must not be {@code null} 243 * 244 * @return a {@link Path} that will be {@linkplain 245 * Value#Value(Supplier, Path) used to build a <code>Value</code>} 246 * to be returned by the {@link #get(Loader, Path)} method; if 247 * {@code null}, then the {@link #get(Loader, Path)} method will 248 * return {@code null} as well 249 * 250 * @exception NullPointerException if either {@code requestor} or 251 * {@code absolutePath} is {@code null} 252 * 253 * @exception IllegalArgumentException if {@code absolutePath} 254 * {@linkplain Path#absolute() is not absolute}, or if {@link 255 * Path#startsWith(Path) 256 * !absolutePath.startsWith(requestor.absolutePath())}, or if {@link 257 * Path#equals(Object) 258 * absolutePath.equals(requestor.absolutePath())} 259 * 260 * @nullability This method does not, but its overrides may, return 261 * {@code null}. 262 * 263 * @idempotency This method is, and its overrides must be, 264 * idempotent and deterministic. 265 * 266 * @threadsafety This method is, and its overrides must be, safe for 267 * concurrent use by multiple threads. 268 * 269 * @see #get(Loader, Path) 270 */ 271 @OverridingEncouraged 272 protected <T extends Type> Path<T> path(final Loader<?> requestor, final Path<T> absolutePath) { 273 return absolutePath; 274 } 275 276}