org.apache.juneau.cp.BeanCreateMethodFinder Maven / Gradle / Ivy
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.cp;
import static org.apache.juneau.common.internal.ArgUtils.*;
import java.util.*;
import java.util.function.*;
import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.reflect.*;
/**
* Utility class for creating beans through creator methods.
*
*
* Used for finding and invoking methods on an object that take in arbitrary parameters and returns bean instances.
*
*
* This class is instantiated through the following methods:
*
* - {@link BeanStore}
*
* - {@link BeanStore#createMethodFinder(Class)}
*
- {@link BeanStore#createMethodFinder(Class,Class)}
*
- {@link BeanStore#createMethodFinder(Class,Object)}
*
*
*
*
* Example:
*
* // The bean we want to create.
* public class A {}
*
* // The bean that has a creator method for the bean above.
* public class B {
*
* // Creator method.
* // Bean store must have a C bean and optionally a D bean.
* public A createA(C c , Optional<D> d ) {
* return new A(c , d .orElse(null ));
* }
* }
*
* // Instantiate the bean with the creator method.
* B b = new B();
*
* // Create a bean store with some mapped beans.
* BeanStore beanStore = BeanStore.create ().addBean(C.class , new C());
*
* // Instantiate the bean using the creator method.
* A a = beanStore
* .createMethodFinder(A.class , b ) // Looking for creator for A on b object.
* .find("createA" ) // Look for method called "createA".
* .thenFind("createA2" ) // Then look for method called "createA2".
* .withDefault(()->new A()) // Optionally supply a default value if method not found.
* .run(); // Execute.
*
*
* See Also:
* - {@link BeanStore}
*
*
* @param The bean type being created.
*/
public class BeanCreateMethodFinder {
private Class beanType;
private final Class> resourceClass;
private final Object resource;
private final BeanStore beanStore;
private MethodInfo method;
private Object[] args;
private Supplier def = ()->null;
BeanCreateMethodFinder(Class beanType, Object resource, BeanStore beanStore) {
this.beanType = assertArgNotNull("beanType", beanType);
this.resource = assertArgNotNull("resource", resource);
this.resourceClass = resource.getClass();
this.beanStore = BeanStore.of(beanStore, resource);
}
BeanCreateMethodFinder(Class beanType, Class> resourceClass, BeanStore beanStore) {
this.beanType = assertArgNotNull("beanType", beanType);
this.resource = null;
this.resourceClass = assertArgNotNull("resourceClass", resourceClass);
this.beanStore = BeanStore.of(beanStore);
}
/**
* Adds a bean to the lookup for parameters.
*
* @param The bean type.
* @param c The bean type.
* @param t The bean.
* @return This object.
*/
public BeanCreateMethodFinder addBean(Class c, T2 t) {
beanStore.addBean(c, t);
return this;
}
/**
* Find the method matching the specified predicate.
*
*
* In order for the method to be used, it must adhere to the following restrictions:
*
* - The method must be public.
*
- The method can be static.
*
- The method name must match exactly.
*
- The method must not be deprecated or annotated with {@link BeanIgnore}.
*
- The method must have all parameter types specified in
requiredParams .
* - The bean store must contain beans for all parameter types.
*
- The bean store may contain beans for all {@link Optional} parameter types.
*
*
*
* This method can be called multiple times with different method names or required parameters until a match is found.
*
Once a method is found, subsequent calls to this method will be no-ops.
*
* See {@link BeanStore#createMethodFinder(Class, Object)} for usage.
*
* @param filter The predicate to apply.
* @return This object.
*/
public BeanCreateMethodFinder find(Predicate filter) {
if (method == null) {
method = ClassInfo.of(resourceClass).getPublicMethod(
x -> x.isNotDeprecated()
&& x.hasReturnType(beanType)
&& x.hasNoAnnotation(BeanIgnore.class)
&& filter.test(x)
&& beanStore.hasAllParams(x)
&& (x.isStatic() || resource != null)
);
if (method != null)
args = beanStore.getParams(method);
}
return this;
}
/**
* Shortcut for calling find(x -> x .hasName(methodName )) .
*
* @param methodName The method name to match.
* @return This object.
*/
public BeanCreateMethodFinder find(String methodName) {
return find(x -> x.hasName(methodName));
}
/**
* Identical to {@link #find(Predicate)} but named for fluent-style calls.
*
* @param filter The predicate to apply.
* @return This object.
*/
public BeanCreateMethodFinder thenFind(Predicate filter) {
return find(filter);
}
/**
* Identical to {@link #find(Predicate)} but named for fluent-style calls.
*
* @param methodName The method name to match.
* @return This object.
*/
public BeanCreateMethodFinder thenFind(String methodName) {
return find(methodName);
}
/**
* A default value to return if no matching methods were found.
*
* @param def The default value. Can be null .
* @return This object.
*/
public BeanCreateMethodFinder withDefault(T def) {
return withDefault(()->def);
}
/**
* A default value to return if no matching methods were found.
*
* @param def The default value.
* @return This object.
*/
public BeanCreateMethodFinder withDefault(Supplier def) {
assertArgNotNull("def", def);
this.def = def;
return this;
}
/**
* Executes the matched method and returns the result.
*
* @return The object returned by the method invocation, or the default value if method was not found.
* @throws ExecutableException If method invocation threw an exception.
*/
@SuppressWarnings("unchecked")
public T run() throws ExecutableException {
if (method != null)
return (T)method.invoke(resource, args);
return def.get();
}
/**
* Same as {@link #run()} but also executes a consumer if the returned value was not null .
*
* @param consumer The consumer of the response.
* @return The object returned by the method invocation, or the default value if method was not found, or {@link Optional#empty()}.
* @throws ExecutableException If method invocation threw an exception.
*/
public T run(Consumer super T> consumer) throws ExecutableException {
T t = run();
if (t != null)
consumer.accept(t);
return t;
}
}