001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.model;
018
019import org.apache.wicket.util.lang.Args;
020import org.danekja.java.util.function.serializable.SerializableBiConsumer;
021import org.danekja.java.util.function.serializable.SerializableConsumer;
022import org.danekja.java.util.function.serializable.SerializableFunction;
023import org.danekja.java.util.function.serializable.SerializableSupplier;
024
025/**
026 * <code>LambdaModel</code> is a basic implementation of an <code>IModel</code> that uses a
027 * serializable {@link java.util.function.Supplier} to get the object and
028 * {@link java.util.function.Consumer} to set it.
029 *
030 * @param <T>
031 *            The type of the Model Object
032 */
033public abstract class LambdaModel<T> implements IModel<T>
034{
035        private static final long serialVersionUID = 1L;
036
037        /**
038         * Constructor hidden, instantiation is done using one of the factory methods
039         */
040        private LambdaModel()
041        {
042        }
043
044        @Override
045        public void setObject(T t)
046        {
047                throw new UnsupportedOperationException("setObject(Object) not supported");
048        }
049
050        /**
051         * Create a read-only {@link IModel}. Usage:
052         * 
053         * <pre>
054         * {@code
055         *     LambdaModel.of(person::getName)
056         * }
057         * </pre>
058         * 
059         * Note that {@link IModel} is a {@code FunctionalInterface} and you can also use a lambda
060         * directly as a model.
061         *
062         * @param getter
063         *            used to get value
064         * @return model
065         *
066         * @param <R>
067         *            model object type
068         */
069        public static <R> IModel<R> of(SerializableSupplier<R> getter)
070        {
071                return getter::get;
072        }
073
074        /**
075         * Create a {@link LambdaModel}. Usage:
076         * 
077         * <pre>
078         * {@code
079         *      LambdaModel.of(person::getName, person::setName)
080         * }
081         * </pre>
082         *
083         * @param getter
084         *            used to get value
085         * @param setter
086         *            used to set value
087         * @return model
088         *
089         * @param <R>
090         *            model object type
091         */
092        public static <R> IModel<R> of(SerializableSupplier<R> getter, SerializableConsumer<R> setter)
093        {
094                Args.notNull(getter, "getter");
095                Args.notNull(setter, "setter");
096
097                return new LambdaModel<R>()
098                {
099                        private static final long serialVersionUID = 1L;
100
101                        @Override
102                        public R getObject()
103                        {
104                                return getter.get();
105                        }
106
107                        @Override
108                        public void setObject(R r)
109                        {
110                                setter.accept(r);
111                        }
112                };
113        }
114
115        /**
116         * Create a {@link LambdaModel} for a given target. Usage:
117         * 
118         * <pre>
119         * {@code
120         *      LambdaModel.of(personModel, Person::getName, Person::setName)
121         * }
122         * </pre>
123         * 
124         * The target model will be detached automatically.
125         *
126         * @param target
127         *            target for getter and setter
128         * @param getter
129         *            used to get a value
130         * @param setter
131         *            used to set a value
132         *
133         * @param <X>
134         *            target model object type
135         * @param <R>
136         *            model object type
137         * 
138         * @return model
139         * @see IModel#flatMap(SerializableFunction)
140         */
141        public static <X, R> IModel<R> of(IModel<X> target, SerializableFunction<X, R> getter,
142                SerializableBiConsumer<X, R> setter)
143        {
144                Args.notNull(target, "target");
145                Args.notNull(getter, "getter");
146                Args.notNull(setter, "setter");
147
148                return new LambdaModel<R>()
149                {
150                        private static final long serialVersionUID = 1L;
151
152                        @Override
153                        public R getObject()
154                        {
155                                X x = target.getObject();
156                                if (x == null)
157                                {
158                                        return null;
159                                }
160                                return getter.apply(x);
161                        }
162
163                        @Override
164                        public void setObject(R r)
165                        {
166                                X x = target.getObject();
167                                if (x != null)
168                                {
169                                        setter.accept(x, r);
170                                }
171                        }
172
173                        @Override
174                        public void detach()
175                        {
176                                target.detach();
177                        }
178                };
179        }
180}