BindUtils.java
001 /*
002  * Copyright 2008-2015 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package griffon.util;
017 
018 import griffon.core.CallableWithArgs;
019 import groovy.lang.Closure;
020 import groovy.util.FactoryBuilderSupport;
021 
022 import javax.annotation.Nonnull;
023 import javax.annotation.Nullable;
024 import java.util.LinkedHashMap;
025 import java.util.Map;
026 
027 import static griffon.util.GriffonNameUtils.isBlank;
028 import static java.util.Objects.requireNonNull;
029 
030 /**
031  * Helper class for constructing bindings between two objects.
032  *
033  @author Andres Almiray
034  @since 2.0.0
035  */
036 public class BindUtils {
037     /**
038      * Create a new Binding using a builder.
039      */
040     @Nonnull
041     public static BindingBuilder binding() {
042         return new BindingBuilder();
043     }
044 
045     public static class BindingBuilder {
046         private Object source;
047         private Object target;
048         private String sourceProperty;
049         private String targetProperty;
050         private CallableWithArgs<?> converter;
051         private CallableWithArgs<?> validator;
052         private boolean mutual;
053 
054         @Nonnull
055         public BindingBuilder withSource(@Nullable Object source) {
056             this.source = source;
057             return this;
058         }
059 
060         @Nonnull
061         public BindingBuilder withTarget(@Nullable Object target) {
062             this.target = target;
063             return this;
064         }
065 
066         @Nonnull
067         public BindingBuilder withSourceProperty(@Nullable String sourceProperty) {
068             this.sourceProperty = sourceProperty;
069             return this;
070         }
071 
072         @Nonnull
073         public BindingBuilder withTargetProperty(@Nullable String targetProperty) {
074             this.targetProperty = targetProperty;
075             return this;
076         }
077 
078         @Nonnull
079         public BindingBuilder withConverter(@Nullable CallableWithArgs<?> converter) {
080             this.converter = converter;
081             return this;
082         }
083 
084         @Nonnull
085         public BindingBuilder withValidator(@Nullable CallableWithArgs<?> validator) {
086             this.validator = validator;
087             return this;
088         }
089 
090         @Nonnull
091         public BindingBuilder withMutual(boolean mutual) {
092             this.mutual = mutual;
093             return this;
094         }
095 
096         public void make(@Nonnull FactoryBuilderSupport builder) {
097             requireNonNull(builder, "Cannot make binding with a null builder!");
098             requireNonNull(source, "Unspecified value for: source");
099             requireNonNull(target, "Unspecified value for: target");
100 
101             Map<String, Object> attributes = new LinkedHashMap<>();
102 
103             if (isBlank(sourceProperty)) sourceProperty = targetProperty;
104             if (isBlank(sourceProperty)) {
105                 throw new IllegalArgumentException("Unspecified values for: sourceProperty, targetProperty");
106             }
107             if (isBlank(targetProperty)) targetProperty = sourceProperty;
108 
109             attributes.put("source", source);
110             attributes.put("target", target);
111             attributes.put("sourceProperty", sourceProperty);
112             attributes.put("targetProperty", targetProperty);
113             attributes.put("mutual", mutual);
114 
115             if (converter != null) {
116                 attributes.put("converter", makeClosure(builder, converter));
117             }
118             if (validator != null) {
119                 attributes.put("validator", makeClosure(builder, validator));
120             }
121 
122             builder.invokeMethod("bind", attributes);
123         }
124 
125         private Closure<?> makeClosure(@Nonnull FactoryBuilderSupport builder, @Nonnull final CallableWithArgs<?> callback) {
126             if (callback instanceof Closure) {
127                 return (Closure<?>callback;
128             }
129             return new Closure<Object>(builder) {
130                 private static final long serialVersionUID = -4108869890482462552L;
131 
132                 @Override
133                 public Object call(Object... args) {
134                     return callback.call(args);
135                 }
136 
137                 @Override
138                 public Object call(Object args) {
139                     return callback.call(args);
140                     // return callback.call(arguments != null && arguments.getClass().isArray() ? (Object[]) arguments : new Object[]{arguments});
141                 }
142             };
143         }
144     }
145 }