001/*
002 * Java Genetic Algorithm Library (jenetics-7.2.0).
003 * Copyright (c) 2007-2023 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020package io.jenetics.internal.util;
021
022import static java.lang.String.format;
023import static java.util.Objects.requireNonNull;
024
025import java.io.Externalizable;
026import java.io.IOException;
027import java.io.InvalidObjectException;
028import java.io.ObjectInput;
029import java.io.ObjectInputStream;
030import java.io.ObjectOutput;
031import java.io.Serial;
032import java.io.Serializable;
033import java.util.Objects;
034import java.util.function.Supplier;
035
036/**
037 * Class for lazy value initialization.
038 *
039 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
040 * @since 3.0
041 * @version 5.0
042 */
043public final class Lazy<T> implements Supplier<T>, Serializable {
044
045        @Serial
046        private static final long serialVersionUID = 2L;
047
048        private final transient Supplier<T> _supplier;
049
050        private T _value;
051        private transient volatile boolean _evaluated;
052
053        private Lazy(
054                final T value,
055                final boolean evaluated,
056                final Supplier<T> supplier
057        ) {
058                _value = value;
059                _evaluated = evaluated;
060                _supplier = supplier;
061        }
062
063        private Lazy(final Supplier<T> supplier) {
064                this(null, false, requireNonNull(supplier));
065        }
066
067        @Override
068        public T get() {
069                return _evaluated ? _value : evaluate();
070        }
071
072        /**
073         * Return the evaluation state of the {@code Lazy} variable.
074         *
075         * @return {@code true} is the {@code Lazy} variable has been evaluated,
076         *         {@code false} otherwise
077         */
078        public boolean isEvaluated() {
079                return _supplier == null || _evaluated || _evaluated();
080        }
081
082        private synchronized boolean _evaluated() {
083                return _evaluated;
084        }
085
086        private synchronized T evaluate() {
087                if (!_evaluated) {
088                        _value = _supplier.get();
089                        _evaluated = true;
090                }
091
092                return _value;
093        }
094
095    @Override
096    public int hashCode() {
097        return Objects.hashCode(get());
098    }
099
100    @Override
101    public boolean equals(final Object obj) {
102                return obj == this ||
103                        obj instanceof Lazy<?> other &&
104                        Objects.equals(other.get(), get());
105    }
106
107    @Override
108    public String toString() {
109                return format("Lazy[%s]", isEvaluated() ? get() : "?");
110    }
111
112        /**
113         * Create a new lazy value initialization.
114         *
115         * @param supplier the lazy value supplier
116         * @param <T> the value type
117         * @return a new lazy value initialization
118         * @throws java.lang.NullPointerException if the given supplier is
119         *         {@code null}
120         */
121        public static <T> Lazy<T> of(final Supplier<T> supplier) {
122                return new Lazy<>(supplier);
123        }
124
125        /**
126         * Create a new {@code Lazy} object with the given {@code value}. This
127         * method allows creating a <em>lazy</em> object with the given
128         * {@code value}.
129         *
130         * @since 3.7
131         *
132         * @param value the value this {@code Lazy} object is initialized with
133         * @param <T> the value type
134         * @return return a new lazy value with the given value
135         */
136        public static <T> Lazy<T> ofValue(final T value) {
137                return new Lazy<>(value, true, null);
138        }
139
140
141        /**************************************************************************
142         *  Java object serialization
143         *************************************************************************/
144
145        static final class SerialProxy implements Externalizable  {
146
147                @Serial
148                private static final long serialVersionUID = 1L;
149
150                private Lazy<?> _object;
151
152                public SerialProxy() {
153                }
154
155                SerialProxy(final Lazy<?> object) {
156                        _object = object;
157                }
158
159                @Serial
160                private Object readResolve() {
161                        return _object;
162                }
163
164                @Override
165                public void writeExternal(final ObjectOutput out) throws IOException {
166                        out.writeObject(_object.get());
167                }
168
169                @Override
170                public void readExternal(ObjectInput in)
171                        throws IOException, ClassNotFoundException
172                {
173                        _object = Lazy.ofValue(in.readObject());
174                }
175        }
176
177        @Serial
178        private Object writeReplace() {
179                return new SerialProxy(this);
180        }
181
182        @Serial
183        private void readObject(final ObjectInputStream stream)
184                throws InvalidObjectException
185        {
186                throw new InvalidObjectException("Serialization proxy required.");
187        }
188
189}