001/* 002 * JDrupes Builder 003 * Copyright (C) 2025 Michael N. Lipp 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <https://www.gnu.org/licenses/>. 017 */ 018 019package org.jdrupes.builder.api; 020 021import java.util.Objects; 022import java.util.Optional; 023 024/// Represents a request for [Resource]s of a specified type. 025/// The specified type provides two kinds of type information: 026/// 027/// 1. The type of the [Resource]s that are actually provided. 028/// 2. The type of the "context" in which the [Resource]s are to be provided. 029/// 030/// As an example, consider requests for a compile time and a runtime 031/// classpath. In both cases, the actually provided [Resource]s are 032/// of type "classpath element". However, depending on the kind of 033/// classpath, a [ResourceProvider] may deliver different collections of 034/// instances of "classpath elements". So instead of requesting 035/// "classpath element", 036/// 037/// Not all requested resource types require context information. For 038/// example, a request for [Cleanliness] usually refers to all resources 039/// that a [Generator] has created and does not depend on a context. 040/// However, in order to keep the API simple, the context is always 041/// required. 042/// 043/// @param <T> the generic type 044/// 045public class ResourceRequest<T extends Resource> { 046 047 private final ResourceType<? extends Resources<T>> type; 048 049 /// Instantiates a new resource request without any restriction. 050 /// 051 /// @param type the requested type 052 /// 053 public ResourceRequest(ResourceType<? extends Resources<T>> type) { 054 this.type = type; 055 } 056 057 /// Creates a request for a resource of the given type, in the 058 /// given container type. The recommended usage pattern is 059 /// to import this method statically. 060 /// 061 /// @param <C> the generic type 062 /// @param <T> the generic type 063 /// @param container the container 064 /// @param requested the requested 065 /// @return the resource request 066 /// 067 public static <C extends Resources<T>, T extends Resource> 068 ResourceRequest<T> 069 requestFor(Class<C> container, Class<T> requested) { 070 return new ResourceRequest<>(new ResourceType<>(container, 071 new ResourceType<>(requested, null))); 072 } 073 074 /// Creates a request for a resource of the given type in a 075 /// container of type [Resources]. The recommended usage pattern 076 /// is to import this method statically. 077 /// 078 /// @param <T> the generic type 079 /// @param requested the requested 080 /// @return the resource request 081 /// 082 public static <T extends Resource> 083 ResourceRequest<T> requestFor(Class<T> requested) { 084 return new ResourceRequest<>(new ResourceType<>(Resources.class, 085 new ResourceType<>(requested, null))); 086 } 087 088 /// Slightly briefer alternative to invoking the constructor. 089 /// 090 /// @param <T> the generic type 091 /// @param type the type 092 /// @return the resource request 093 /// 094 public static <T extends Resource> ResourceRequest<T> 095 requestFor(ResourceType<? extends Resources<T>> type) { 096 return new ResourceRequest<>(type); 097 } 098 099 /// Create a widened resource request by replacing the requested 100 /// top-level type with the given super type, thus widening the 101 /// request. 102 /// 103 /// @param <R> the generic type 104 /// @param type the desired super type. This should actually be 105 /// declared as `Class <R>`, but there is no way to specify a 106 /// parameterized type as actual parameter. 107 /// @return the new resource request 108 /// 109 public <R extends Resources<T>> ResourceRequest<T> widened( 110 @SuppressWarnings("rawtypes") Class<? extends Resources> type) { 111 return new ResourceRequest<>(type().widened(type)); 112 } 113 114 /// Return the requested type. 115 /// 116 /// @return the resource type 117 /// 118 public ResourceType<? extends Resources<T>> type() { 119 return type; 120 } 121 122 /// Checks if this request accepts a resource of the given type. 123 /// Short for `type().isAssignableFrom(other)`. 124 /// 125 /// @param other the other 126 /// @return true, if successful 127 /// 128 public boolean accepts(ResourceType<?> other) { 129 return type().isAssignableFrom(other); 130 } 131 132 /// Checks if the requested type is a container type and if the 133 /// contained type of the container type is assignable from the 134 /// given type. 135 /// 136 /// @param type the type to check 137 /// @return true, if successful 138 /// 139 public boolean collects(ResourceType<?> type) { 140 return Optional.ofNullable(type().containedType()) 141 .map(ct -> ct.isAssignableFrom(type)).orElse(false); 142 } 143 144 @Override 145 public int hashCode() { 146 return Objects.hash(type); 147 } 148 149 @Override 150 public boolean equals(Object obj) { 151 if (this == obj) { 152 return true; 153 } 154 if (obj == null) { 155 return false; 156 } 157 if (getClass() != obj.getClass()) { 158 return false; 159 } 160 ResourceRequest<?> other = (ResourceRequest<?>) obj; 161 return Objects.equals(type, other.type); 162 } 163 164 @Override 165 public String toString() { 166 return "ResourceRequest [type=" + type + "]"; 167 } 168 169}