001 /*
002 * Copyright 2008-2014 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 }
|