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