001 /*
002 * Copyright 2008-2017 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 final class BindUtils {
037 private BindUtils() {
038 // prevent instantiation
039 }
040
041 /**
042 * Create a new Binding using a builder.
043 */
044 @Nonnull
045 public static BindingBuilder binding() {
046 return new BindingBuilder();
047 }
048
049 public static class BindingBuilder {
050 private Object source;
051 private Object target;
052 private String sourceProperty;
053 private String targetProperty;
054 private CallableWithArgs<?> converter;
055 private CallableWithArgs<?> validator;
056 private boolean mutual;
057
058 @Nonnull
059 public BindingBuilder withSource(@Nullable Object source) {
060 this.source = source;
061 return this;
062 }
063
064 @Nonnull
065 public BindingBuilder withTarget(@Nullable Object target) {
066 this.target = target;
067 return this;
068 }
069
070 @Nonnull
071 public BindingBuilder withSourceProperty(@Nullable String sourceProperty) {
072 this.sourceProperty = sourceProperty;
073 return this;
074 }
075
076 @Nonnull
077 public BindingBuilder withTargetProperty(@Nullable String targetProperty) {
078 this.targetProperty = targetProperty;
079 return this;
080 }
081
082 @Nonnull
083 public BindingBuilder withConverter(@Nullable CallableWithArgs<?> converter) {
084 this.converter = converter;
085 return this;
086 }
087
088 @Nonnull
089 public BindingBuilder withValidator(@Nullable CallableWithArgs<?> validator) {
090 this.validator = validator;
091 return this;
092 }
093
094 @Nonnull
095 public BindingBuilder withMutual(boolean mutual) {
096 this.mutual = mutual;
097 return this;
098 }
099
100 public void make(@Nonnull FactoryBuilderSupport builder) {
101 requireNonNull(builder, "Cannot make binding with a null builder!");
102 requireNonNull(source, "Unspecified value for: source");
103 requireNonNull(target, "Unspecified value for: target");
104
105 Map<String, Object> attributes = new LinkedHashMap<>();
106
107 if (isBlank(sourceProperty)) sourceProperty = targetProperty;
108 if (isBlank(sourceProperty)) {
109 throw new IllegalArgumentException("Unspecified values for: sourceProperty, targetProperty");
110 }
111 if (isBlank(targetProperty)) targetProperty = sourceProperty;
112
113 attributes.put("source", source);
114 attributes.put("target", target);
115 attributes.put("sourceProperty", sourceProperty);
116 attributes.put("targetProperty", targetProperty);
117 attributes.put("mutual", mutual);
118
119 if (converter != null) {
120 attributes.put("converter", makeClosure(builder, converter));
121 }
122 if (validator != null) {
123 attributes.put("validator", makeClosure(builder, validator));
124 }
125
126 builder.invokeMethod("bind", attributes);
127 }
128
129 private Closure<?> makeClosure(@Nonnull FactoryBuilderSupport builder, @Nonnull final CallableWithArgs<?> callback) {
130 if (callback instanceof Closure) {
131 return (Closure<?>) callback;
132 }
133 return new Closure<Object>(builder) {
134 private static final long serialVersionUID = -4108869890482462552L;
135
136 @Override
137 public Object call(Object... args) {
138 return callback.call(args);
139 }
140
141 @Override
142 public Object call(Object args) {
143 return callback.call(args);
144 }
145 };
146 }
147 }
148 }
|