001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2025 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
006 * the License. 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 distributed under the License is distributed on
011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
012 * specific language governing permissions and limitations under the License.
013 */
014package org.microbean.attributes;
015
016import java.util.Arrays;
017import java.util.List;
018
019import java.lang.constant.ClassDesc;
020import java.lang.constant.DynamicConstantDesc;
021import java.lang.constant.MethodHandleDesc;
022import java.lang.constant.MethodTypeDesc;
023
024import java.util.Optional;
025
026import org.microbean.constant.Constables;
027
028import static java.lang.constant.ConstantDescs.BSM_INVOKE;
029import static java.lang.constant.ConstantDescs.CD_List;
030import static java.lang.constant.ConstantDescs.CD_Object;
031import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC;
032import static java.lang.constant.DirectMethodHandleDesc.Kind.VIRTUAL;
033
034/**
035 * A {@link Value} holding other {@link Value}s.
036 *
037 * @param <T> the type of the {@link Value}s
038 *
039 * @param value a non-{@code null} array of {@link Value}s
040 *
041 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
042 */
043public final record ArrayValue<T extends Value<T>>(T[] value) implements Value<ArrayValue<T>> {
044
045  @SuppressWarnings("rawtypes")
046  private static final Value<?>[] EMPTY_VALUE_ARRAY = new Value[0];
047
048  /**
049   * Creates a new {@link ArrayValue}.
050   *
051   * @param value a non-{@code null} array of {@link Value}s
052   *
053   * @exception NullPointerException if {@code value} is {@code null}
054   */
055  public ArrayValue {
056    value = value.clone();
057  }
058
059  @Override // Comparable<ArrayValue<T>>
060  public final int compareTo(final ArrayValue<T> other) {
061    if (other == null) {
062      return -1;
063    } else if (this.equals(other)) {
064      return 0;
065    }
066    return this.toString().compareTo(other.toString());
067  }
068
069  @Override // Constable
070  public final Optional<DynamicConstantDesc<ArrayValue<T>>> describeConstable() {
071    final ClassDesc me = ClassDesc.of(this.getClass().getName());
072    return Constables.describeConstable(Arrays.asList(this.value()))
073      .map(valueDesc -> DynamicConstantDesc.of(BSM_INVOKE,
074                                               MethodHandleDesc.ofMethod(STATIC,
075                                                                         me,
076                                                                         "of",
077                                                                         MethodTypeDesc.of(me,
078                                                                                           CD_List)),
079                                               valueDesc));
080  }
081
082  @Override // Record
083  public final boolean equals(final Object other) {
084    return
085      other == this ||
086      // Follow java.lang.annotation.Annotation requirements.
087      other != null && other.getClass() == this.getClass() && Arrays.equals(this.value(), ((ArrayValue<?>)other).value());
088  }
089
090  @Override // Record
091  public final int hashCode() {
092    // Follow java.lang.annotation.Annotation requirements.
093    return Arrays.hashCode(this.value());
094  }
095
096  @Override // Record
097  public final String toString() {
098    return Arrays.asList(this.value()).toString();
099  }
100
101  /**
102   * Returns the array of suitably-typed {@link Value}s this {@link ArrayValue} holds.
103   *
104   * <p>No reference to the returned array is kept.</p>
105   *
106   * @return a non-{@code null} array of suitably-typed {@link Value}s
107   */
108  @Override // Record
109  public final T[] value() {
110    return this.value.clone();
111  }
112
113  /**
114   * Returns an {@link ArrayValue} suitable for the supplied argument.
115   *
116   * @param values a non-{@code null} array of values
117   *
118   * @return a non-{@code null} {@link ArrayValue}
119   *
120   * @exception NullPointerException if {@code values} is {@code null}
121   *
122   * @see #of(Value[])
123   */
124  public static final ArrayValue<BooleanValue> of(final boolean... values) {
125    final var va = new BooleanValue[values.length];
126    for (int i = 0; i < values.length; i++) {
127      va[i] = BooleanValue.of(values[i]);
128    }
129    return new ArrayValue<>(va);
130  }
131
132  /**
133   * Returns an {@link ArrayValue} suitable for the supplied argument.
134   *
135   * @param values a non-{@code null} array of values
136   *
137   * @return a non-{@code null} {@link ArrayValue}
138   *
139   * @exception NullPointerException if {@code values} is {@code null}
140   *
141   * @see #of(Value[])
142   */
143  public static final ArrayValue<ByteValue> of(final byte... values) {
144    final var va = new ByteValue[values.length];
145    for (int i = 0; i < values.length; i++) {
146      va[i] = ByteValue.of(values[i]);
147    }
148    return new ArrayValue<>(va);
149  }
150
151  /**
152   * Returns an {@link ArrayValue} suitable for the supplied argument.
153   *
154   * @param values a non-{@code null} array of values
155   *
156   * @return a non-{@code null} {@link ArrayValue}
157   *
158   * @exception NullPointerException if {@code values} is {@code null}
159   *
160   * @see #of(Value[])
161   */
162  public static final ArrayValue<CharValue> of(final char... values) {
163    final var va = new CharValue[values.length];
164    for (int i = 0; i < values.length; i++) {
165      va[i] = CharValue.of(values[i]);
166    }
167    return new ArrayValue<>(va);
168  }
169
170  /**
171   * Returns an {@link ArrayValue} suitable for the supplied argument.
172   *
173   * @param values a non-{@code null} array of values
174   *
175   * @return a non-{@code null} {@link ArrayValue}
176   *
177   * @exception NullPointerException if {@code values} is {@code null}
178   *
179   * @see #of(Value[])
180   */
181  public static final ArrayValue<DoubleValue> of(final double... values) {
182    final var va = new DoubleValue[values.length];
183    for (int i = 0; i < values.length; i++) {
184      va[i] = DoubleValue.of(values[i]);
185    }
186    return new ArrayValue<>(va);
187  }
188
189  /**
190   * Returns an {@link ArrayValue} suitable for the supplied argument.
191   *
192   * @param values a non-{@code null} array of values
193   *
194   * @return a non-{@code null} {@link ArrayValue}
195   *
196   * @exception NullPointerException if {@code values} is {@code null}
197   *
198   * @see #of(Value[])
199   */
200  public static final ArrayValue<FloatValue> of(final float... values) {
201    final var va = new FloatValue[values.length];
202    for (int i = 0; i < values.length; i++) {
203      va[i] = FloatValue.of(values[i]);
204    }
205    return new ArrayValue<>(va);
206  }
207
208  /**
209   * Returns an {@link ArrayValue} suitable for the supplied argument.
210   *
211   * @param values a non-{@code null} array of values
212   *
213   * @return a non-{@code null} {@link ArrayValue}
214   *
215   * @exception NullPointerException if {@code values} is {@code null}
216   *
217   * @see #of(Value[])
218   */
219  public static final ArrayValue<IntValue> of(final int... values) {
220    final var va = new IntValue[values.length];
221    for (int i = 0; i < values.length; i++) {
222      va[i] = IntValue.of(values[i]);
223    }
224    return new ArrayValue<>(va);
225  }
226
227  /**
228   * Returns an {@link ArrayValue} suitable for the supplied argument.
229   *
230   * @param values a non-{@code null} array of values
231   *
232   * @return a non-{@code null} {@link ArrayValue}
233   *
234   * @exception NullPointerException if {@code values} is {@code null}
235   *
236   * @see #of(Value[])
237   */
238  public static final ArrayValue<LongValue> of(final long... values) {
239    final var va = new LongValue[values.length];
240    for (int i = 0; i < values.length; i++) {
241      va[i] = LongValue.of(values[i]);
242    }
243    return new ArrayValue<>(va);
244  }
245
246  /**
247   * Returns an {@link ArrayValue} suitable for the supplied argument.
248   *
249   * @param values a non-{@code null} array of values
250   *
251   * @return a non-{@code null} {@link ArrayValue}
252   *
253   * @exception NullPointerException if {@code values} is {@code null}
254   *
255   * @see #of(Value[])
256   */
257  public static final ArrayValue<ShortValue> of(final short... values) {
258    final var va = new ShortValue[values.length];
259    for (int i = 0; i < values.length; i++) {
260      va[i] = ShortValue.of(values[i]);
261    }
262    return new ArrayValue<>(va);
263  }
264
265  /**
266   * Returns an {@link ArrayValue} suitable for the supplied argument.
267   *
268   * @param values a non-{@code null} array of values
269   *
270   * @return a non-{@code null} {@link ArrayValue}
271   *
272   * @exception NullPointerException if {@code values} is {@code null}
273   *
274   * @see #of(Value[])
275   */
276  public static final ArrayValue<ClassValue> of(final Class<?>... values) {
277    final var va = new ClassValue[values.length];
278    for (int i = 0; i < values.length; i++) {
279      va[i] = ClassValue.of(values[i]);
280    }
281    return new ArrayValue<>(va);
282  }
283
284  /**
285   * Returns an {@link ArrayValue} suitable for the supplied argument.
286   *
287   * @param values a non-{@code null} array of values
288   *
289   * @return a non-{@code null} {@link ArrayValue}
290   *
291   * @exception NullPointerException if {@code values} is {@code null}
292   *
293   * @see #of(Value[])
294   */
295  public static final ArrayValue<StringValue> of(final String... values) {
296    final var va = new StringValue[values.length];
297    for (int i = 0; i < values.length; i++) {
298      va[i] = StringValue.of(values[i]);
299    }
300    return new ArrayValue<>(va);
301  }
302
303  /**
304   * Returns an {@link ArrayValue} suitable for the supplied argument.
305   *
306   * @param <T> the type of {@link Value} held by the returned {@link ArrayValue}
307   *
308   * @param value a non-{@code null} {@link List} of suitably-typed {@link Value}s
309   *
310   * @return a non-{@code null} {@link ArrayValue}
311   *
312   * @exception NullPointerException if {@code value} is {@code null}
313   *
314   * @see #of(Value[])
315   */
316  @SuppressWarnings("unchecked")
317  // Called by #describeConstable()
318  public static final <T extends Value<T>> ArrayValue<T> of(final List<T> value) {
319    return of(value.toArray((T[])EMPTY_VALUE_ARRAY));
320  }
321
322  /**
323   * Returns an {@link ArrayValue} suitable for the supplied argument.
324   *
325   * @param <T> the type of {@link Value} held by the returned {@link ArrayValue}
326   *
327   * @param value a non-{@code null} array of suitably-typed {@link Value}s
328   *
329   * @return a non-{@code null} {@link ArrayValue}
330   *
331   * @exception NullPointerException if {@code value} is {@code null}
332   */
333  public static final <T extends Value<T>> ArrayValue<T> of(final T[] value) {
334    return new ArrayValue<>(value);
335  }
336
337}