in sdk/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsFactory.java [978:1099]
private static List<PropertyDescriptor> validateClass(Class<? extends PipelineOptions> iface,
Set<Class<? extends PipelineOptions>> validatedPipelineOptionsInterfaces,
Class<?> klass) throws IntrospectionException {
Set<Method> methods = Sets.newHashSet(IGNORED_METHODS);
// Ignore static methods, "equals", "hashCode", "toString" and "as" on the generated class.
for (Method method : klass.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) {
methods.add(method);
}
}
try {
methods.add(klass.getMethod("equals", Object.class));
methods.add(klass.getMethod("hashCode"));
methods.add(klass.getMethod("toString"));
methods.add(klass.getMethod("as", Class.class));
methods.add(klass.getMethod("cloneAs", Class.class));
} catch (NoSuchMethodException | SecurityException e) {
throw Throwables.propagate(e);
}
// Verify that there are no methods with the same name with two different return types.
Iterable<Method> interfaceMethods = FluentIterable
.from(ReflectHelpers.getClosureOfMethodsOnInterface(iface))
.toSortedSet(MethodComparator.INSTANCE);
SortedSetMultimap<Method, Method> methodNameToMethodMap =
TreeMultimap.create(MethodNameComparator.INSTANCE, MethodComparator.INSTANCE);
for (Method method : interfaceMethods) {
methodNameToMethodMap.put(method, method);
}
for (Map.Entry<Method, Collection<Method>> entry
: methodNameToMethodMap.asMap().entrySet()) {
Set<Class<?>> returnTypes = FluentIterable.from(entry.getValue())
.transform(ReturnTypeFetchingFunction.INSTANCE).toSet();
SortedSet<Method> collidingMethods = FluentIterable.from(entry.getValue())
.toSortedSet(MethodComparator.INSTANCE);
Preconditions.checkArgument(returnTypes.size() == 1,
"Method [%s] has multiple definitions %s with different return types for [%s].",
entry.getKey().getName(),
collidingMethods,
iface.getName());
}
// Verify that there is no getter with a mixed @JsonIgnore annotation and verify
// that no setter has @JsonIgnore.
Iterable<Method> allInterfaceMethods = FluentIterable
.from(ReflectHelpers.getClosureOfMethodsOnInterfaces(validatedPipelineOptionsInterfaces))
.append(ReflectHelpers.getClosureOfMethodsOnInterface(iface))
.toSortedSet(MethodComparator.INSTANCE);
SortedSetMultimap<Method, Method> methodNameToAllMethodMap =
TreeMultimap.create(MethodNameComparator.INSTANCE, MethodComparator.INSTANCE);
for (Method method : allInterfaceMethods) {
methodNameToAllMethodMap.put(method, method);
}
List<PropertyDescriptor> descriptors = getPropertyDescriptors(klass);
for (PropertyDescriptor descriptor : descriptors) {
if (descriptor.getReadMethod() == null
|| descriptor.getWriteMethod() == null
|| IGNORED_METHODS.contains(descriptor.getReadMethod())
|| IGNORED_METHODS.contains(descriptor.getWriteMethod())) {
continue;
}
SortedSet<Method> getters = methodNameToAllMethodMap.get(descriptor.getReadMethod());
SortedSet<Method> gettersWithJsonIgnore = Sets.filter(getters, JsonIgnorePredicate.INSTANCE);
Iterable<String> getterClassNames = FluentIterable.from(getters)
.transform(MethodToDeclaringClassFunction.INSTANCE)
.transform(ReflectHelpers.CLASS_NAME);
Iterable<String> gettersWithJsonIgnoreClassNames = FluentIterable.from(gettersWithJsonIgnore)
.transform(MethodToDeclaringClassFunction.INSTANCE)
.transform(ReflectHelpers.CLASS_NAME);
Preconditions.checkArgument(gettersWithJsonIgnore.isEmpty()
|| getters.size() == gettersWithJsonIgnore.size(),
"Expected getter for property [%s] to be marked with @JsonIgnore on all %s, "
+ "found only on %s",
descriptor.getName(), getterClassNames, gettersWithJsonIgnoreClassNames);
SortedSet<Method> settersWithJsonIgnore =
Sets.filter(methodNameToAllMethodMap.get(descriptor.getWriteMethod()),
JsonIgnorePredicate.INSTANCE);
Iterable<String> settersWithJsonIgnoreClassNames = FluentIterable.from(settersWithJsonIgnore)
.transform(MethodToDeclaringClassFunction.INSTANCE)
.transform(ReflectHelpers.CLASS_NAME);
Preconditions.checkArgument(settersWithJsonIgnore.isEmpty(),
"Expected setter for property [%s] to not be marked with @JsonIgnore on %s",
descriptor.getName(), settersWithJsonIgnoreClassNames);
}
// Verify that each property has a matching read and write method.
for (PropertyDescriptor propertyDescriptor : descriptors) {
Preconditions.checkArgument(
IGNORED_METHODS.contains(propertyDescriptor.getWriteMethod())
|| propertyDescriptor.getReadMethod() != null,
"Expected getter for property [%s] of type [%s] on [%s].",
propertyDescriptor.getName(),
propertyDescriptor.getPropertyType().getName(),
iface.getName());
Preconditions.checkArgument(
IGNORED_METHODS.contains(propertyDescriptor.getReadMethod())
|| propertyDescriptor.getWriteMethod() != null,
"Expected setter for property [%s] of type [%s] on [%s].",
propertyDescriptor.getName(),
propertyDescriptor.getPropertyType().getName(),
iface.getName());
methods.add(propertyDescriptor.getReadMethod());
methods.add(propertyDescriptor.getWriteMethod());
}
// Verify that no additional methods are on an interface that aren't a bean property.
SortedSet<Method> unknownMethods = new TreeSet<>(MethodComparator.INSTANCE);
unknownMethods.addAll(Sets.difference(Sets.newHashSet(klass.getMethods()), methods));
Preconditions.checkArgument(unknownMethods.isEmpty(),
"Methods %s on [%s] do not conform to being bean properties.",
FluentIterable.from(unknownMethods).transform(ReflectHelpers.METHOD_FORMATTER),
iface.getName());
return descriptors;
}