org.springframework.util.ObjectUtils Java Examples

The following examples show how to use org.springframework.util.ObjectUtils. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: PropertyMatches.java    From lams with GNU General Public License v2.0 6 votes vote down vote up
@Override
public String buildErrorMessage() {
	String propertyName = getPropertyName();
	String[] possibleMatches = getPossibleMatches();
	StringBuilder msg = new StringBuilder();
	msg.append("Bean property '");
	msg.append(propertyName);
	msg.append("' is not writable or has an invalid setter method. ");

	if (ObjectUtils.isEmpty(possibleMatches)) {
		msg.append("Does the parameter type of the setter match the return type of the getter?");
	}
	else {
		appendHintMessage(msg);
	}
	return msg.toString();
}
 
Example #2
Source File: ScriptTemplateView.java    From lams with GNU General Public License v2.0 6 votes vote down vote up
protected ScriptEngine getEngine() {
	if (Boolean.FALSE.equals(this.sharedEngine)) {
		Map<Object, ScriptEngine> engines = enginesHolder.get();
		if (engines == null) {
			engines = new HashMap<Object, ScriptEngine>(4);
			enginesHolder.set(engines);
		}
		Object engineKey = (!ObjectUtils.isEmpty(this.scripts) ?
				new EngineKey(this.engineName, this.scripts) : this.engineName);
		ScriptEngine engine = engines.get(engineKey);
		if (engine == null) {
			engine = createEngineFromName();
			engines.put(engineKey, engine);
		}
		return engine;
	}
	else {
		// Simply return the configured ScriptEngine...
		return this.engine;
	}
}
 
Example #3
Source File: OrderComparator.java    From spring4-understanding with Apache License 2.0 6 votes vote down vote up
/**
 * Determine the order value for the given object.
 * <p>The default implementation checks against the given {@link OrderSourceProvider}
 * using {@link #findOrder} and falls back to a regular {@link #getOrder(Object)} call.
 * @param obj the object to check
 * @return the order value, or {@code Ordered.LOWEST_PRECEDENCE} as fallback
 */
private int getOrder(Object obj, OrderSourceProvider sourceProvider) {
	Integer order = null;
	if (sourceProvider != null) {
		Object orderSource = sourceProvider.getOrderSource(obj);
		if (orderSource != null && orderSource.getClass().isArray()) {
			Object[] sources = ObjectUtils.toObjectArray(orderSource);
			for (Object source : sources) {
				order = findOrder(source);
				if (order != null) {
					break;
				}
			}
		}
		else {
			order = findOrder(orderSource);
		}
	}
	return (order != null ? order : getOrder(obj));
}
 
Example #4
Source File: BootTimeSeriesMiniCubeController.java    From minicubes with Apache License 2.0 6 votes vote down vote up
@RequestMapping(value="/groupsum", method={RequestMethod.POST, RequestMethod.GET})
public @ResponseBody Map<Integer, BigDecimal> groupsum(@NotBlank @RequestParam String indName, 
        @RequestParam(required=false) String filterDims,
        @RequestParam String groupbyDim,
        @NotBlank @RequestParam String... timeSeries) throws Throwable {
    
    LOGGER.info("Try to sum {} on {} with filter {}.", indName, ObjectUtils.getDisplayString(timeSeries), filterDims);
    long timing = System.currentTimeMillis();
    Map<String, List<Integer>> filter = (filterDims == null || "".equals(filterDims)) ? null
            : objectMapper.readValue(filterDims, new TypeReference<Map<String, List<Integer>>>() {});
    Map<Integer, BigDecimal> sum = manager.aggs(timeSeries).sum(indName, groupbyDim, filter);
    LOGGER.info("Sucess to sum {} on {} result size is {} using {}ms.", indName, timeSeries, sum.size(), System.currentTimeMillis() - timing);
    LOGGER.debug("Sucess to sum {} on {} result is {}.", indName, timeSeries, sum);
    
    return sum;
}
 
Example #5
Source File: RecursiveAnnotationArrayVisitor.java    From java-technology-stack with MIT License 6 votes vote down vote up
@Override
public void visit(String attributeName, Object attributeValue) {
	Object newValue = attributeValue;
	Object existingValue = this.attributes.get(this.attributeName);
	if (existingValue != null) {
		newValue = ObjectUtils.addObjectToArray((Object[]) existingValue, newValue);
	}
	else {
		Class<?> arrayClass = newValue.getClass();
		if (Enum.class.isAssignableFrom(arrayClass)) {
			while (arrayClass.getSuperclass() != null && !arrayClass.isEnum()) {
				arrayClass = arrayClass.getSuperclass();
			}
		}
		Object[] newArray = (Object[]) Array.newInstance(arrayClass, 1);
		newArray[0] = newValue;
		newValue = newArray;
	}
	this.attributes.put(this.attributeName, newValue);
}
 
Example #6
Source File: ReflectionTestUtils.java    From java-technology-stack with MIT License 6 votes vote down vote up
/**
 * Invoke the method with the given {@code name} on the supplied target
 * object with the supplied arguments.
 * <p>This method traverses the class hierarchy in search of the desired
 * method. In addition, an attempt will be made to make non-{@code public}
 * methods <em>accessible</em>, thus allowing one to invoke {@code protected},
 * {@code private}, and <em>package-private</em> methods.
 * @param target the target object on which to invoke the specified method
 * @param name the name of the method to invoke
 * @param args the arguments to provide to the method
 * @return the invocation result, if any
 * @see MethodInvoker
 * @see ReflectionUtils#makeAccessible(Method)
 * @see ReflectionUtils#invokeMethod(Method, Object, Object[])
 * @see ReflectionUtils#handleReflectionException(Exception)
 */
@SuppressWarnings("unchecked")
@Nullable
public static <T> T invokeMethod(Object target, String name, Object... args) {
	Assert.notNull(target, "Target object must not be null");
	Assert.hasText(name, "Method name must not be empty");

	try {
		MethodInvoker methodInvoker = new MethodInvoker();
		methodInvoker.setTargetObject(target);
		methodInvoker.setTargetMethod(name);
		methodInvoker.setArguments(args);
		methodInvoker.prepare();

		if (logger.isDebugEnabled()) {
			logger.debug(String.format("Invoking method '%s' on %s with arguments %s", name, safeToString(target),
					ObjectUtils.nullSafeToString(args)));
		}

		return (T) methodInvoker.invoke();
	}
	catch (Exception ex) {
		ReflectionUtils.handleReflectionException(ex);
		throw new IllegalStateException("Should never get here");
	}
}
 
Example #7
Source File: Jaxb2Marshaller.java    From spring4-understanding with Apache License 2.0 6 votes vote down vote up
@Override
public void afterPropertiesSet() throws Exception {
	boolean hasContextPath = StringUtils.hasLength(this.contextPath);
	boolean hasClassesToBeBound = !ObjectUtils.isEmpty(this.classesToBeBound);
	boolean hasPackagesToScan = !ObjectUtils.isEmpty(this.packagesToScan);

	if (hasContextPath && (hasClassesToBeBound || hasPackagesToScan) ||
			(hasClassesToBeBound && hasPackagesToScan)) {
		throw new IllegalArgumentException("Specify either 'contextPath', 'classesToBeBound', " +
				"or 'packagesToScan'");
	}
	if (!hasContextPath && !hasClassesToBeBound && !hasPackagesToScan) {
		throw new IllegalArgumentException(
				"Setting either 'contextPath', 'classesToBeBound', " + "or 'packagesToScan' is required");
	}
	if (!this.lazyInit) {
		getJaxbContext();
	}
	if (!ObjectUtils.isEmpty(this.schemaResources)) {
		this.schema = loadSchema(this.schemaResources, this.schemaLanguage);
	}
}
 
Example #8
Source File: GenericXmlContextLoaderResourceLocationsTests.java    From spring4-understanding with Apache License 2.0 6 votes vote down vote up
@Test
public void assertContextConfigurationLocations() throws Exception {

	final ContextConfiguration contextConfig = this.testClass.getAnnotation(ContextConfiguration.class);
	final ContextLoader contextLoader = new GenericXmlContextLoader();
	final String[] configuredLocations = (String[]) AnnotationUtils.getValue(contextConfig);
	final String[] processedLocations = contextLoader.processLocations(this.testClass, configuredLocations);

	if (logger.isDebugEnabled()) {
		logger.debug("----------------------------------------------------------------------");
		logger.debug("Configured locations: " + ObjectUtils.nullSafeToString(configuredLocations));
		logger.debug("Expected   locations: " + ObjectUtils.nullSafeToString(this.expectedLocations));
		logger.debug("Processed  locations: " + ObjectUtils.nullSafeToString(processedLocations));
	}

	assertArrayEquals("Verifying locations for test [" + this.testClass + "].", this.expectedLocations,
		processedLocations);
}
 
Example #9
Source File: ScriptTemplateView.java    From spring4-understanding with Apache License 2.0 6 votes vote down vote up
protected void loadScripts(ScriptEngine engine) {
	if (!ObjectUtils.isEmpty(this.scripts)) {
		try {
			for (String script : this.scripts) {
				Resource resource = this.resourceLoader.getResource(script);
				if (!resource.exists()) {
					throw new IllegalStateException("Script resource [" + script + "] not found");
				}
				engine.eval(new InputStreamReader(resource.getInputStream()));
			}
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to load script", ex);
		}
	}
}
 
Example #10
Source File: SpringCloudCircuitBreakerBuildCustomizer.java    From start.spring.io with Apache License 2.0 6 votes vote down vote up
private void removeBlockingCloudResilience4j(Build build) {
	Dependency cloudResilience4j = this.metadata.getDependencies().get("cloud-resilience4j");
	if (cloudResilience4j.getBom() != null) {
		BillOfMaterials bom = resolveBom(cloudResilience4j.getBom());
		if (bom != null) {
			build.boms().add(cloudResilience4j.getBom());
			if (bom.getVersionProperty() != null) {
				build.properties().version(bom.getVersionProperty(), bom.getVersion());
			}
			if (!ObjectUtils.isEmpty(bom.getRepositories())) {
				bom.getRepositories().forEach((repository) -> build.repositories().add(repository));
			}
		}
	}
	if (cloudResilience4j.getRepository() != null) {
		build.repositories().add(cloudResilience4j.getRepository());
	}
	build.dependencies().remove("cloud-resilience4j");
}
 
Example #11
Source File: ScriptTemplateView.java    From spring4-understanding with Apache License 2.0 6 votes vote down vote up
protected ScriptEngine getEngine() {
	if (Boolean.FALSE.equals(this.sharedEngine)) {
		Map<Object, ScriptEngine> engines = enginesHolder.get();
		if (engines == null) {
			engines = new HashMap<Object, ScriptEngine>(4);
			enginesHolder.set(engines);
		}
		Object engineKey = (!ObjectUtils.isEmpty(this.scripts) ?
				new EngineKey(this.engineName, this.scripts) : this.engineName);
		ScriptEngine engine = engines.get(engineKey);
		if (engine == null) {
			engine = createEngineFromName();
			engines.put(engineKey, engine);
		}
		return engine;
	}
	else {
		// Simply return the configured ScriptEngine...
		return this.engine;
	}
}
 
Example #12
Source File: StreamAnnotationCommonMethodUtils.java    From spring-cloud-stream with Apache License 2.0 6 votes vote down vote up
public static String getOutboundBindingTargetName(Method method) {
	SendTo sendTo = AnnotationUtils.findAnnotation(method, SendTo.class);
	if (sendTo != null) {
		Assert.isTrue(!ObjectUtils.isEmpty(sendTo.value()),
				StreamAnnotationErrorMessages.ATLEAST_ONE_OUTPUT);
		Assert.isTrue(sendTo.value().length == 1,
				StreamAnnotationErrorMessages.SEND_TO_MULTIPLE_DESTINATIONS);
		Assert.hasText(sendTo.value()[0],
				StreamAnnotationErrorMessages.SEND_TO_EMPTY_DESTINATION);
		return sendTo.value()[0];
	}
	Output output = AnnotationUtils.findAnnotation(method, Output.class);
	if (output != null) {
		Assert.isTrue(StringUtils.hasText(output.value()),
				StreamAnnotationErrorMessages.ATLEAST_ONE_OUTPUT);
		return output.value();
	}
	return null;
}
 
Example #13
Source File: SendToMethodReturnValueHandler.java    From spring4-understanding with Apache License 2.0 6 votes vote down vote up
protected String[] getTargetDestinations(Annotation annotation, Message<?> message, String defaultPrefix) {
	if (annotation != null) {
		String[] value = (String[]) AnnotationUtils.getValue(annotation);
		if (!ObjectUtils.isEmpty(value)) {
			return value;
		}
	}
	String name = DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER;
	String destination = (String) message.getHeaders().get(name);
	if (!StringUtils.hasText(destination)) {
		throw new IllegalStateException("No lookup destination header in " + message);
	}

	return (destination.startsWith("/") ?
			new String[] {defaultPrefix + destination} : new String[] {defaultPrefix + "/" + destination});
}
 
Example #14
Source File: SimpleMessageConverter.java    From spring4-understanding with Apache License 2.0 6 votes vote down vote up
/**
 * This implementation creates a TextMessage for a String, a
 * BytesMessage for a byte array, a MapMessage for a Map,
 * and an ObjectMessage for a Serializable object.
 * @see #createMessageForString
 * @see #createMessageForByteArray
 * @see #createMessageForMap
 * @see #createMessageForSerializable
 */
@Override
public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
	if (object instanceof Message) {
		return (Message) object;
	}
	else if (object instanceof String) {
		return createMessageForString((String) object, session);
	}
	else if (object instanceof byte[]) {
		return createMessageForByteArray((byte[]) object, session);
	}
	else if (object instanceof Map) {
		return createMessageForMap((Map<? ,?>) object, session);
	}
	else if (object instanceof Serializable) {
		return createMessageForSerializable(((Serializable) object), session);
	}
	else {
		throw new MessageConversionException("Cannot convert object of type [" +
				ObjectUtils.nullSafeClassName(object) + "] to JMS message. Supported message " +
				"payloads are: String, byte array, Map<String,?>, Serializable object.");
	}
}
 
Example #15
Source File: UriComponentsBuilder.java    From java-technology-stack with MIT License 5 votes vote down vote up
/**
 * Set the query parameter values overriding all existing query values for
 * the same parameter. If no values are given, the query parameter is removed.
 * @param name the query parameter name
 * @param values the query parameter values
 * @return this UriComponentsBuilder
 */
@Override
public UriComponentsBuilder replaceQueryParam(String name, Object... values) {
	Assert.notNull(name, "Name must not be null");
	this.queryParams.remove(name);
	if (!ObjectUtils.isEmpty(values)) {
		queryParam(name, values);
	}
	resetSchemeSpecificPart();
	return this;
}
 
Example #16
Source File: MockHttpServletRequestBuilder.java    From spring-analysis-note with MIT License 5 votes vote down vote up
private boolean containsCookie(Cookie cookie) {
	for (Cookie cookieToCheck : this.cookies) {
		if (ObjectUtils.nullSafeEquals(cookieToCheck.getName(), cookie.getName())) {
			return true;
		}
	}
	return false;
}
 
Example #17
Source File: PropertyDescriptorUtils.java    From spring-analysis-note with MIT License 5 votes vote down vote up
/**
 * Compare the given {@code PropertyDescriptors} and return {@code true} if
 * they are equivalent, i.e. their read method, write method, property type,
 * property editor and flags are equivalent.
 * @see java.beans.PropertyDescriptor#equals(Object)
 */
public static boolean equals(PropertyDescriptor pd, PropertyDescriptor otherPd) {
	return (ObjectUtils.nullSafeEquals(pd.getReadMethod(), otherPd.getReadMethod()) &&
			ObjectUtils.nullSafeEquals(pd.getWriteMethod(), otherPd.getWriteMethod()) &&
			ObjectUtils.nullSafeEquals(pd.getPropertyType(), otherPd.getPropertyType()) &&
			ObjectUtils.nullSafeEquals(pd.getPropertyEditorClass(), otherPd.getPropertyEditorClass()) &&
			pd.isBound() == otherPd.isBound() && pd.isConstrained() == otherPd.isConstrained());
}
 
Example #18
Source File: MatchAlwaysTransactionAttributeSource.java    From spring-analysis-note with MIT License 5 votes vote down vote up
@Override
public boolean equals(Object other) {
	if (this == other) {
		return true;
	}
	if (!(other instanceof MatchAlwaysTransactionAttributeSource)) {
		return false;
	}
	MatchAlwaysTransactionAttributeSource otherTas = (MatchAlwaysTransactionAttributeSource) other;
	return ObjectUtils.nullSafeEquals(this.transactionAttribute, otherTas.transactionAttribute);
}
 
Example #19
Source File: BeanDefinitionBuilder.java    From lams with GNU General Public License v2.0 5 votes vote down vote up
/**
 * Append the specified bean name to the list of beans that this definition
 * depends on.
 */
public BeanDefinitionBuilder addDependsOn(String beanName) {
	if (this.beanDefinition.getDependsOn() == null) {
		this.beanDefinition.setDependsOn(beanName);
	}
	else {
		String[] added = ObjectUtils.addObjectToArray(this.beanDefinition.getDependsOn(), beanName);
		this.beanDefinition.setDependsOn(added);
	}
	return this;
}
 
Example #20
Source File: KeyValueTemplateTestsUsingHazelcastTest.java    From spring-data-hazelcast with Apache License 2.0 5 votes vote down vote up
@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (!(obj instanceof Foo)) {
        return false;
    }
    Foo other = (Foo) obj;
    return ObjectUtils.nullSafeEquals(this.foo, other.foo);
}
 
Example #21
Source File: BeanDefinitionHolder.java    From spring4-understanding with Apache License 2.0 5 votes vote down vote up
@Override
public boolean equals(Object other) {
	if (this == other) {
		return true;
	}
	if (!(other instanceof BeanDefinitionHolder)) {
		return false;
	}
	BeanDefinitionHolder otherHolder = (BeanDefinitionHolder) other;
	return this.beanDefinition.equals(otherHolder.beanDefinition) &&
			this.beanName.equals(otherHolder.beanName) &&
			ObjectUtils.nullSafeEquals(this.aliases, otherHolder.aliases);
}
 
Example #22
Source File: LogController.java    From Taroco with Apache License 2.0 5 votes vote down vote up
/**
 * 分页查询日志信息
 *
 * @param params 分页对象
 * @return 分页对象
 */
@GetMapping("/logPage")
@RequireRole(RoleConst.ADMIN)
public Page logPage(@RequestParam Map<String, Object> params) {
    final QueryWrapper<SysLog> query = new QueryWrapper<>();
    query.eq(CommonConstant.DEL_FLAG, CommonConstant.STATUS_NORMAL);
    final String typeKey = "type";
    if (params.containsKey(typeKey) && !ObjectUtils.isEmpty(params.get(typeKey))) {
        query.eq(typeKey, params.get(typeKey));
    }
    return (Page) sysLogService.page(new PageQuery<>(params), query);
}
 
Example #23
Source File: ToStringCreatorTests.java    From java-technology-stack with MIT License 5 votes vote down vote up
@Test
public void defaultStyleMap() {
	final Map<String, String> map = getMap();
	Object stringy = new Object() {
		@Override
		public String toString() {
			return new ToStringCreator(this).append("familyFavoriteSport", map).toString();
		}
	};
	assertEquals("[ToStringCreatorTests.4@" + ObjectUtils.getIdentityHexString(stringy) +
			" familyFavoriteSport = map['Keri' -> 'Softball', 'Scot' -> 'Fishing', 'Keith' -> 'Flag Football']]",
			stringy.toString());
}
 
Example #24
Source File: GigaSpacesModelValidator.java    From cacheonix-core with GNU Lesser General Public License v2.1 5 votes vote down vote up
/**
 * @see AbstractCacheModelValidator#validateFlushingModelProperties(Object)
 * @throws InvalidCacheModelException
 *           if the given model does not specify at least one cache.
 */
protected void validateFlushingModelProperties(Object flushingModel)
    throws InvalidCacheModelException {
  GigaSpacesFlushingModel model = (GigaSpacesFlushingModel) flushingModel;
  String[] cacheNames = model.getCacheNames();

  if (ObjectUtils.isEmpty(cacheNames)) {
    throw new InvalidCacheModelException(
        "There should be at least one cache name");
  }
}
 
Example #25
Source File: UnsatisfiedServletRequestParameterException.java    From java-technology-stack with MIT License 5 votes vote down vote up
private static String requestParameterMapToString(Map<String, String[]> actualParams) {
	StringBuilder result = new StringBuilder();
	for (Iterator<Map.Entry<String, String[]>> it = actualParams.entrySet().iterator(); it.hasNext();) {
		Map.Entry<String, String[]> entry = it.next();
		result.append(entry.getKey()).append('=').append(ObjectUtils.nullSafeToString(entry.getValue()));
		if (it.hasNext()) {
			result.append(", ");
		}
	}
	return result.toString();
}
 
Example #26
Source File: CacheAspectSupport.java    From spring4-understanding with Apache License 2.0 5 votes vote down vote up
private Object[] extractArgs(Method method, Object[] args) {
	if (!method.isVarArgs()) {
		return args;
	}
	Object[] varArgs = ObjectUtils.toObjectArray(args[args.length - 1]);
	Object[] combinedArgs = new Object[args.length - 1 + varArgs.length];
	System.arraycopy(args, 0, combinedArgs, 0, args.length - 1);
	System.arraycopy(varArgs, 0, combinedArgs, args.length - 1, varArgs.length);
	return combinedArgs;
}
 
Example #27
Source File: TypedStringValue.java    From spring-analysis-note with MIT License 5 votes vote down vote up
@Override
public boolean equals(Object other) {
	if (this == other) {
		return true;
	}
	if (!(other instanceof TypedStringValue)) {
		return false;
	}
	TypedStringValue otherValue = (TypedStringValue) other;
	return (ObjectUtils.nullSafeEquals(this.value, otherValue.value) &&
			ObjectUtils.nullSafeEquals(this.targetType, otherValue.targetType));
}
 
Example #28
Source File: BeanDefinitionValueResolver.java    From spring4-understanding with Apache License 2.0 5 votes vote down vote up
/**
 * Evaluate the given value as an expression, if necessary.
 * @param value the candidate value (may be an expression)
 * @return the resolved value
 */
protected Object evaluate(TypedStringValue value) {
	Object result = doEvaluate(value.getValue());
	if (!ObjectUtils.nullSafeEquals(result, value.getValue())) {
		value.setDynamic();
	}
	return result;
}
 
Example #29
Source File: AopProxyUtils.java    From spring-analysis-note with MIT License 5 votes vote down vote up
/**
 * Adapt the given arguments to the target signature in the given method,
 * if necessary: in particular, if a given vararg argument array does not
 * match the array type of the declared vararg parameter in the method.
 * @param method the target method
 * @param arguments the given arguments
 * @return a cloned argument array, or the original if no adaptation is needed
 * @since 4.2.3
 */
static Object[] adaptArgumentsIfNecessary(Method method, @Nullable Object[] arguments) {
	if (ObjectUtils.isEmpty(arguments)) {
		return new Object[0];
	}
	if (method.isVarArgs()) {
		Class<?>[] paramTypes = method.getParameterTypes();
		if (paramTypes.length == arguments.length) {
			int varargIndex = paramTypes.length - 1;
			Class<?> varargType = paramTypes[varargIndex];
			if (varargType.isArray()) {
				Object varargArray = arguments[varargIndex];
				if (varargArray instanceof Object[] && !varargType.isInstance(varargArray)) {
					Object[] newArguments = new Object[arguments.length];
					System.arraycopy(arguments, 0, newArguments, 0, varargIndex);
					Class<?> targetElementType = varargType.getComponentType();
					int varargLength = Array.getLength(varargArray);
					Object newVarargArray = Array.newInstance(targetElementType, varargLength);
					System.arraycopy(varargArray, 0, newVarargArray, 0, varargLength);
					newArguments[varargIndex] = newVarargArray;
					return newArguments;
				}
			}
		}
	}
	return arguments;
}
 
Example #30
Source File: DataBinder.java    From spring4-understanding with Apache License 2.0 5 votes vote down vote up
/**
 * Add a custom formatter, applying it to the specified field types only, if any,
 * or otherwise to all fields matching the {@link Formatter}-declared type.
 * <p>Registers a corresponding {@link PropertyEditor} adapter underneath the covers.
 * @param formatter the formatter to add (does not need to generically declare a
 * field type if field types are explicitly specified as parameters)
 * @param fieldTypes the field types to apply the formatter to, or none if to be
 * derived from the given {@link Formatter} implementation class
 * @since 4.2
 * @see #registerCustomEditor(Class, PropertyEditor)
 */
public void addCustomFormatter(Formatter<?> formatter, Class<?>... fieldTypes) {
	FormatterPropertyEditorAdapter adapter = new FormatterPropertyEditorAdapter(formatter);
	if (ObjectUtils.isEmpty(fieldTypes)) {
		getPropertyEditorRegistry().registerCustomEditor(adapter.getFieldType(), adapter);
	}
	else {
		for (Class<?> fieldType : fieldTypes) {
			getPropertyEditorRegistry().registerCustomEditor(fieldType, adapter);
		}
	}
}