in juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java [178:508]
String init(BeanMeta<T> beanMeta) {
Class<?> c = classMeta.getInnerClass();
ClassInfo ci = classMeta.getInfo();
try {
Visibility
conVis = ctx.getBeanConstructorVisibility(),
cVis = ctx.getBeanClassVisibility(),
mVis = ctx.getBeanMethodVisibility(),
fVis = ctx.getBeanFieldVisibility();
List<Class<?>> bdClasses = list();
if (beanFilter != null && beanFilter.getBeanDictionary() != null)
addAll(bdClasses, beanFilter.getBeanDictionary());
Value<String> typeName = Value.empty();
classMeta.forEachAnnotation(Bean.class, x -> isNotEmpty(x.typeName()), x -> typeName.set(x.typeName()));
if (typeName.isPresent())
bdClasses.add(classMeta.innerClass);
this.beanRegistry = new BeanRegistry(ctx, null, bdClasses.toArray(new Class<?>[bdClasses.size()]));
Value<String> typePropertyName = Value.empty();
classMeta.forEachAnnotation(Bean.class, x -> isNotEmpty(x.typePropertyName()), x -> typePropertyName.set(x.typePropertyName()));
this.typePropertyName = typePropertyName.orElseGet(()->ctx.getBeanTypePropertyName());
fluentSetters = (ctx.isFindFluentSetters() || (beanFilter != null && beanFilter.isFluentSetters()));
// If @Bean.interfaceClass is specified on the parent class, then we want
// to use the properties defined on that class, not the subclass.
Class<?> c2 = (beanFilter != null && beanFilter.getInterfaceClass() != null ? beanFilter.getInterfaceClass() : c);
Class<?> stopClass = (beanFilter != null ? beanFilter.getStopClass() : Object.class);
if (stopClass == null)
stopClass = Object.class;
Map<String,BeanPropertyMeta.Builder> normalProps = map();
boolean hasBean = ci.hasAnnotation(ctx, Bean.class);
boolean hasBeanIgnore = ci.hasAnnotation(ctx, BeanIgnore.class);
/// See if this class matches one the patterns in the exclude-class list.
if (ctx.isNotABean(c))
return "Class matches exclude-class list";
if (! hasBean && ! (cVis.isVisible(c.getModifiers()) || c.isAnonymousClass()))
return "Class is not public";
if (hasBeanIgnore)
return "Class is annotated with @BeanIgnore";
// Make sure it's serializable.
if (beanFilter == null && ctx.isBeansRequireSerializable() && ! ci.isChildOf(Serializable.class))
return "Class is not serializable";
// Look for @Beanc constructor on public constructors.
ci.forEachPublicConstructor(x -> x.hasAnnotation(ctx, Beanc.class), x -> {
if (constructor != null)
throw new BeanRuntimeException(c, "Multiple instances of '@Beanc' found.");
constructor = x;
constructorArgs = new String[0];
ctx.forEachAnnotation(Beanc.class, x.inner(), y -> ! y.properties().isEmpty(), z -> constructorArgs = split(z.properties()));
if (! x.hasNumParams(constructorArgs.length)) {
if (constructorArgs.length != 0)
throw new BeanRuntimeException(c, "Number of properties defined in '@Beanc' annotation does not match number of parameters in constructor.");
constructorArgs = new String[x.getParamCount()];
IntValue i = IntValue.create();
x.forEachParam(null, pi -> {
String pn = pi.getName();
if (pn == null)
throw new BeanRuntimeException(c, "Could not find name for parameter #{0} of constructor ''{1}''", i, x.getFullName());
constructorArgs[i.getAndIncrement()] = pn;
});
}
constructor.setAccessible();
});
// Look for @Beanc on all other constructors.
if (constructor == null) {
ci.forEachDeclaredConstructor(x -> x.hasAnnotation(ctx, Beanc.class), x -> {
if (constructor != null)
throw new BeanRuntimeException(c, "Multiple instances of '@Beanc' found.");
constructor = x;
constructorArgs = new String[0];
ctx.forEachAnnotation(Beanc.class, x.inner(), y -> ! y.properties().isEmpty(), z -> constructorArgs = split(z.properties()));
if (! x.hasNumParams(constructorArgs.length)) {
if (constructorArgs.length != 0)
throw new BeanRuntimeException(c, "Number of properties defined in '@Beanc' annotation does not match number of parameters in constructor.");
constructorArgs = new String[x.getParamCount()];
IntValue i = IntValue.create();
x.forEachParam(null, y -> {
String pn = y.getName();
if (pn == null)
throw new BeanRuntimeException(c, "Could not find name for parameter #{0} of constructor ''{1}''", i, x.getFullName());
constructorArgs[i.getAndIncrement()] = pn;
});
}
constructor.setAccessible();
});
}
// If this is an interface, look for impl classes defined in the context.
if (constructor == null)
constructor = implClassConstructor;
if (constructor == null)
constructor = ci.getNoArgConstructor(hasBean ? Visibility.PRIVATE : conVis);
if (constructor == null && beanFilter == null && ctx.isBeansRequireDefaultConstructor())
return "Class does not have the required no-arg constructor";
if (constructor != null)
constructor.setAccessible();
// Explicitly defined property names in @Bean annotation.
Set<String> fixedBeanProps = set();
Set<String> bpi = set();
Set<String> bpx = set();
Set<String> bpro = set();
Set<String> bpwo = set();
Set<String> filterProps = set(); // Names of properties defined in @Bean(properties)
if (beanFilter != null) {
Set<String> bfbpi = beanFilter.getProperties();
filterProps.addAll(bfbpi);
// Get the 'properties' attribute if specified.
if (bpi.isEmpty())
fixedBeanProps.addAll(bfbpi);
if (beanFilter.getPropertyNamer() != null)
propertyNamer = beanFilter.getPropertyNamer();
bpro.addAll(beanFilter.getReadOnlyProperties());
bpwo.addAll(beanFilter.getWriteOnlyProperties());
}
fixedBeanProps.addAll(bpi);
if (propertyNamer == null)
propertyNamer = ctx.getPropertyNamer();
// First populate the properties with those specified in the bean annotation to
// ensure that ordering first.
fixedBeanProps.forEach(x -> normalProps.put(x, BeanPropertyMeta.builder(beanMeta, x)));
if (ctx.isUseJavaBeanIntrospector()) {
BeanInfo bi = null;
if (! c2.isInterface())
bi = Introspector.getBeanInfo(c2, stopClass);
else
bi = Introspector.getBeanInfo(c2, null);
if (bi != null) {
for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
String name = pd.getName();
if (! normalProps.containsKey(name))
normalProps.put(name, BeanPropertyMeta.builder(beanMeta, name));
normalProps.get(name).setGetter(pd.getReadMethod()).setSetter(pd.getWriteMethod());
}
}
} else /* Use 'better' introspection */ {
findBeanFields(ctx, c2, stopClass, fVis).forEach(x -> {
String name = findPropertyName(x);
if (name != null) {
if (! normalProps.containsKey(name))
normalProps.put(name, BeanPropertyMeta.builder(beanMeta, name));
normalProps.get(name).setField(x);
}
});
List<BeanMethod> bms = findBeanMethods(ctx, c2, stopClass, mVis, propertyNamer, fluentSetters);
// Iterate through all the getters.
bms.forEach(x -> {
String pn = x.propertyName;
Method m = x.method;
if (! normalProps.containsKey(pn))
normalProps.put(pn, new BeanPropertyMeta.Builder(beanMeta, pn));
BeanPropertyMeta.Builder bpm = normalProps.get(pn);
if (x.methodType == GETTER) {
// Two getters. Pick the best.
if (bpm.getter != null) {
if (! ctx.hasAnnotation(Beanp.class, m) && ctx.hasAnnotation(Beanp.class, bpm.getter))
m = bpm.getter; // @Beanp annotated method takes precedence.
else if (m.getName().startsWith("is") && bpm.getter.getName().startsWith("get"))
m = bpm.getter; // getX() overrides isX().
}
bpm.setGetter(m);
}
});
// Now iterate through all the setters.
bms.forEach(x -> {
if (x.methodType == SETTER) {
BeanPropertyMeta.Builder bpm = normalProps.get(x.propertyName);
if (x.matchesPropertyType(bpm))
bpm.setSetter(x.method);
}
});
// Now iterate through all the extraKeys.
bms.forEach(x -> {
if (x.methodType == EXTRAKEYS) {
BeanPropertyMeta.Builder bpm = normalProps.get(x.propertyName);
bpm.setExtraKeys(x.method);
}
});
}
typeVarImpls = map();
findTypeVarImpls(c, typeVarImpls);
if (typeVarImpls.isEmpty())
typeVarImpls = null;
// Eliminate invalid properties, and set the contents of getterProps and setterProps.
for (Iterator<BeanPropertyMeta.Builder> i = normalProps.values().iterator(); i.hasNext();) {
BeanPropertyMeta.Builder p = i.next();
try {
if (p.field == null)
p.setInnerField(findInnerBeanField(ctx, c, stopClass, p.name));
if (p.validate(ctx, beanRegistry, typeVarImpls, bpro, bpwo)) {
if (p.getter != null)
getterProps.put(p.getter, p.name);
if (p.setter != null)
setterProps.put(p.setter, p.name);
} else {
i.remove();
}
} catch (ClassNotFoundException e) {
throw new BeanRuntimeException(c, e.getLocalizedMessage());
}
}
// Check for missing properties.
fixedBeanProps.forEach(x -> {
if (! normalProps.containsKey(x))
throw new BeanRuntimeException(c, "The property ''{0}'' was defined on the @Bean(properties=X) annotation of class ''{1}'' but was not found on the class definition.", x, ci.getSimpleName());
});
// Mark constructor arg properties.
for (String fp : constructorArgs) {
BeanPropertyMeta.Builder m = normalProps.get(fp);
if (m == null)
throw new BeanRuntimeException(c, "The property ''{0}'' was defined on the @Beanc(properties=X) annotation but was not found on the class definition.", fp);
m.setAsConstructorArg();
}
// Make sure at least one property was found.
if (beanFilter == null && ctx.isBeansRequireSomeProperties() && normalProps.isEmpty())
return "No properties detected on bean class";
sortProperties = (ctx.isSortProperties() || (beanFilter != null && beanFilter.isSortProperties())) && fixedBeanProps.isEmpty();
properties = sortProperties ? sortedMap() : map();
if (beanFilter != null && beanFilter.getTypeName() != null)
dictionaryName = beanFilter.getTypeName();
if (dictionaryName == null)
dictionaryName = findDictionaryName(this.classMeta);
normalProps.forEach((k,v) -> {
BeanPropertyMeta pMeta = v.build();
if (pMeta.isDyna())
dynaProperty = pMeta;
properties.put(k, pMeta);
});
// If a beanFilter is defined, look for inclusion and exclusion lists.
if (beanFilter != null) {
// Eliminated excluded properties if BeanFilter.excludeKeys is specified.
Set<String> bfbpi = beanFilter.getProperties();
Set<String> bfbpx = beanFilter.getExcludeProperties();
if (bpi.isEmpty() && ! bfbpi.isEmpty()) {
// Only include specified properties if BeanFilter.includeKeys is specified.
// Note that the order must match includeKeys.
Map<String,BeanPropertyMeta> properties2 = map();
bfbpi.forEach(x -> {
if (properties.containsKey(x))
properties2.put(x, properties.remove(x));
});
hiddenProperties.putAll(properties);
properties = properties2;
}
if (bpx.isEmpty() && ! bfbpx.isEmpty()) {
bfbpx.forEach(x -> hiddenProperties.put(x, properties.remove(x)));
}
}
if (! bpi.isEmpty()) {
Map<String,BeanPropertyMeta> properties2 = map();
bpi.forEach(x -> {
if (properties.containsKey(x))
properties2.put(x, properties.remove(x));
});
hiddenProperties.putAll(properties);
properties = properties2;
}
bpx.forEach(x -> hiddenProperties.put(x, properties.remove(x)));
if (pNames != null) {
Map<String,BeanPropertyMeta> properties2 = map();
for (String k : pNames) {
if (properties.containsKey(k))
properties2.put(k, properties.get(k));
else
hiddenProperties.put(k, properties.get(k));
}
properties = properties2;
}
} catch (BeanRuntimeException e) {
throw e;
} catch (Exception e) {
return "Exception: " + getStackTrace(e);
}
return null;
}