![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.juneau.AnnotationApplier 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;
import static org.apache.juneau.common.internal.StringUtils.*;
import static org.apache.juneau.internal.ClassUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import java.lang.annotation.*;
import java.nio.charset.*;
import java.util.*;
import java.util.stream.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.common.internal.StringUtils;
import org.apache.juneau.reflect.*;
import org.apache.juneau.svl.*;
/**
* Class used to add properties to a context builder (e.g. {@link BeanContext.Builder}) from an annotation (e.g. {@link BeanConfig}).
*
*
* Used by {@link Context.Builder#applyAnnotations(Class...)} and {@link Context.Builder#applyAnnotations(java.lang.reflect.Method...)} to apply
* annotations to context beans.
*
*
* The following code shows the general design pattern.
*
*
* // The annotation applied to classes and methods.
* @Target ({METHOD,TYPE})
* @Retention (RUNTIME )
* @ContextApply (BeanConfigAnnotationApplier.class )
* public @interface BeanConfig {
*
* String sortProperties() default "" ;
*
* }
*
* // The applier that applies the annotation to the bean context builder.
* public class BeanConfigAnnotationApplier extends AnnotationApplier<BeanConfig ,BeanContext.Builder> {
*
* // Required constructor.
* public Applier(VarResolverSession vr ) {
* super (BeanConfig.class , BeanContext.Builder.class , vr );
* }
*
* @Override
* public void apply(AnnotationInfo<BeanConfig> annotationInfo , BeanContext.Builder builder ) {
* BeanConfig beanConfig = annotationInfo .getAnnotation();
*
* String sortProperties = beanConfig .sortProperties();
* if (! sortProperties .isEmpty())
* builder .sortProperties(Boolean.parseBoolean (sortProperties ));
* }
* }
*
* // An annotated class.
* @BeanConfig (sortProperties="true" )
* public class AnnotatedClass {}
*
* // Putting it together.
* public static void main(String[] args ) {
*
* // Create a JSON serializer with sorted properties.
* Serializer serializer = JsonSerializer.create ().applyAnnotations(AnnotatedClass.class ).build();
* }
*
*
* See Also:
*
*
* @param The annotation that this applier reads from.
* @param The builder class to apply the annotation to.
*/
public abstract class AnnotationApplier {
private final VarResolverSession vr;
private final Class ca;
private final Class cb;
/**
* Constructor.
*
* @param annotationClass The annotation class.
* @param builderClass The annotation class.
* @param vr The string resolver to use for resolving strings.
*/
protected AnnotationApplier(Class annotationClass, Class builderClass, VarResolverSession vr) {
this.vr = vr == null ? VarResolver.DEFAULT.createSession() : vr;
this.ca = annotationClass;
this.cb = builderClass;
}
/**
* Apply the specified annotation to the specified property store builder.
*
* @param annotationInfo The annotation.
* @param builder The property store builder.
*/
public abstract void apply(AnnotationInfo annotationInfo, B builder);
/**
* Returns true if this apply can be appied to the specified builder.
*
* @param builder The builder to check.
* @return true if this apply can be appied to the specified builder.
*/
public boolean canApply(Object builder) {
return cb.isInstance(builder);
}
/**
* Returns the builder class that this applier applies to.
*
* @return The builder class that this applier applies to.
*/
public Class> getBuilderClass() {
return cb;
}
/**
* Returns the var resolver session for this apply.
*
* @return The var resolver session for this apply.
*/
protected VarResolverSession vr() {
return vr;
}
/**
* Resolves the specified string.
*
* @param in The string containing variables to resolve.
* @return An optional containing the specified string if it exists, or {@link Optional#empty()} if it does not.
*/
protected Optional string(String in) {
in = vr.resolve(in);
return optional(isEmpty(in) ? null : in);
}
/**
* Returns the specified value if it's simple name is not "void" .
*
* @param The value to return.
* @param in The value to return.
* @return An optional containing the specified value.
*/
protected Optional> type(Class in) {
return optional(in).filter(NOT_VOID);
}
/**
* Returns the specified string array as an {@link Optional}.
*
*
* If the array is empty, then returns {@link Optional#empty()}.
*
* @param in The string array.
* @return The array wrapped in an {@link Optional}.
*/
protected Optional strings(String[] in) {
return optional(in.length == 0 ? null : Arrays.stream(in).map(x -> vr.resolve(x)).filter(StringUtils::isNotEmpty).toArray(String[]::new));
}
/**
* Resolves the specified string as a comma-delimited list of strings.
*
* @param in The CDL string containing variables to resolve.
* @return An array with resolved strings.
*/
protected Stream stream(String[] in) {
return Arrays.stream(in).map(x -> vr.resolve(x)).filter(StringUtils::isNotEmpty);
}
/**
* Resolves the specified string as a comma-delimited list of strings.
*
* @param in The CDL string containing variables to resolve.
* @return An array with resolved strings.
*/
protected Stream cdl(String in) {
return Arrays.stream(split(vr.resolve(in))).filter(StringUtils::isNotEmpty);
}
/**
* Resolves the specified string and converts it to a boolean.
*
* @param in The string containing variables to resolve.
* @return The resolved boolean.
*/
public Optional bool(String in) {
return string(in).map(Boolean::parseBoolean);
}
/**
* Resolves the specified string and converts it to an int.
*
* @param in The string containing variables to resolve.
* @param loc The annotation field name.
* @return The resolved int.
*/
protected Optional integer(String in, String loc) {
try {
return string(in).map(Integer::parseInt);
} catch (NumberFormatException e) {
throw new ConfigException("Invalid syntax for integer on annotation @{0}({1}): {2}", ca.getSimpleName(), loc, in);
}
}
/**
* Resolves the specified string and converts it to a Charset.
*
* @param in The string containing variables to resolve.
* @return The resolved Charset.
*/
protected Optional charset(String in) {
return string(in).map(x -> "default".equalsIgnoreCase(x) ? Charset.defaultCharset() : Charset.forName(x));
}
/**
* Resolves the specified string and converts it to a Character.
*
* @param in The string containing variables to resolve.
* @param loc The annotation field name.
* @return The resolved Character.
*/
protected Optional character(String in, String loc) {
return string(in).map(x -> toCharacter(x, loc));
}
private Character toCharacter(String in, String loc) {
if (in.length() != 1)
throw new ConfigException("Invalid syntax for character on annotation @{0}({1}): {2}", ca.getSimpleName(), loc, in);
return in.charAt(0);
}
/**
* Returns the specified class array as an {@link Optional}.
*
*
* If the array is empty, then returns {@link Optional#empty()}.
*
* @param in The class array.
* @return The array wrapped in an {@link Optional}.
*/
protected Optional[]> classes(Class>[] in) {
return optional(in.length == 0 ? null : in);
}
/**
* Represents a no-op configuration apply.
*/
public static class NoOp extends AnnotationApplier {
/**
* Constructor.
*
* @param r The string resolver to use for resolving strings.
*/
public NoOp(VarResolverSession r) {
super(Annotation.class, Object.class, r);
}
@Override /* ConfigApply */
public void apply(AnnotationInfo ai, Object b) {}
}
}