/*
* Copyright 2008-2017 the original author or authors.
*
* Licensed 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 griffon.util;
import griffon.core.CallableWithArgs;
import groovy.lang.Closure;
import groovy.util.FactoryBuilderSupport;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.LinkedHashMap;
import java.util.Map;
import static griffon.util.GriffonNameUtils.isBlank;
import static java.util.Objects.requireNonNull;
/**
* Helper class for constructing bindings between two objects.
*
* @author Andres Almiray
* @since 2.0.0
*/
public class BindUtils {
/**
* Create a new Binding using a builder.
*/
@Nonnull
public static BindingBuilder binding() {
return new BindingBuilder();
}
public static class BindingBuilder {
private Object source;
private Object target;
private String sourceProperty;
private String targetProperty;
private CallableWithArgs<?> converter;
private CallableWithArgs<?> validator;
private boolean mutual;
@Nonnull
public BindingBuilder withSource(@Nullable Object source) {
this.source = source;
return this;
}
@Nonnull
public BindingBuilder withTarget(@Nullable Object target) {
this.target = target;
return this;
}
@Nonnull
public BindingBuilder withSourceProperty(@Nullable String sourceProperty) {
this.sourceProperty = sourceProperty;
return this;
}
@Nonnull
public BindingBuilder withTargetProperty(@Nullable String targetProperty) {
this.targetProperty = targetProperty;
return this;
}
@Nonnull
public BindingBuilder withConverter(@Nullable CallableWithArgs<?> converter) {
this.converter = converter;
return this;
}
@Nonnull
public BindingBuilder withValidator(@Nullable CallableWithArgs<?> validator) {
this.validator = validator;
return this;
}
@Nonnull
public BindingBuilder withMutual(boolean mutual) {
this.mutual = mutual;
return this;
}
public void make(@Nonnull FactoryBuilderSupport builder) {
requireNonNull(builder, "Cannot make binding with a null builder!");
requireNonNull(source, "Unspecified value for: source");
requireNonNull(target, "Unspecified value for: target");
Map<String, Object> attributes = new LinkedHashMap<>();
if (isBlank(sourceProperty)) sourceProperty = targetProperty;
if (isBlank(sourceProperty)) {
throw new IllegalArgumentException("Unspecified values for: sourceProperty, targetProperty");
}
if (isBlank(targetProperty)) targetProperty = sourceProperty;
attributes.put("source", source);
attributes.put("target", target);
attributes.put("sourceProperty", sourceProperty);
attributes.put("targetProperty", targetProperty);
attributes.put("mutual", mutual);
if (converter != null) {
attributes.put("converter", makeClosure(builder, converter));
}
if (validator != null) {
attributes.put("validator", makeClosure(builder, validator));
}
builder.invokeMethod("bind", attributes);
}
private Closure<?> makeClosure(@Nonnull FactoryBuilderSupport builder, @Nonnull final CallableWithArgs<?> callback) {
if (callback instanceof Closure) {
return (Closure<?>) callback;
}
return new Closure<Object>(builder) {
private static final long serialVersionUID = -4108869890482462552L;
@Override
public Object call(Object... args) {
return callback.call(args);
}
@Override
public Object call(Object args) {
return callback.call(args);
// return callback.call(arguments != null && arguments.getClass().isArray() ? (Object[]) arguments : new Object[]{arguments});
}
};
}
}
}