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.DirectMethodHandleDesc.Kind.STATIC;
031import static java.lang.constant.DirectMethodHandleDesc.Kind.VIRTUAL;
032import static java.util.Arrays.asList;
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 *
043 * @see ListValue
044 */
045public final record ArrayValue<T extends Value<T>>(T[] value) implements Value<ArrayValue<T>> {
046
047
048  /*
049   * Static fields.
050   */
051
052
053  @SuppressWarnings("rawtypes")
054  private static final Value<?>[] EMPTY_VALUE_ARRAY = new Value[0];
055
056
057  /*
058   * Constructors.
059   */
060
061
062  /**
063   * Creates a new {@link ArrayValue}.
064   *
065   * @param value a non-{@code null} array of {@link Value}s
066   *
067   * @exception NullPointerException if {@code value} is {@code null}
068   */
069  public ArrayValue {
070    value = value.clone();
071  }
072
073
074  /*
075   * Instance methods.
076   */
077
078
079  @Override // Comparable<ArrayValue<T>>
080  public final int compareTo(final ArrayValue<T> other) {
081    if (other == null) {
082      return -1;
083    } else if (this.equals(other)) {
084      return 0;
085    }
086    return this.toString().compareTo(other.toString());
087  }
088
089  @Override // Constable
090  public final Optional<DynamicConstantDesc<ArrayValue<T>>> describeConstable() {
091    final ClassDesc me = ClassDesc.of(this.getClass().getName());
092    return Constables.describeConstable(asList(this.value()))
093      .map(valueDesc -> DynamicConstantDesc.of(BSM_INVOKE,
094                                               MethodHandleDesc.ofMethod(STATIC,
095                                                                         me,
096                                                                         "of",
097                                                                         MethodTypeDesc.of(me,
098                                                                                           CD_List)),
099                                               valueDesc));
100  }
101
102  @Override // Record
103  public final boolean equals(final Object other) {
104    return
105      other == this ||
106      // Follow java.lang.annotation.Annotation requirements.
107      other != null && other.getClass() == this.getClass() && Arrays.equals(this.value(), ((ArrayValue<?>)other).value());
108  }
109
110  @Override // Record
111  public final int hashCode() {
112    // Follow java.lang.annotation.Annotation requirements.
113    return Arrays.hashCode(this.value());
114  }
115
116  @Override // Record
117  public final String toString() {
118    return asList(this.value()).toString();
119  }
120
121  /**
122   * Returns a clone of the array of suitably-typed {@link Value}s this {@link ArrayValue} holds.
123   *
124   * @return a non-{@code null} array of suitably-typed {@link Value}s
125   */
126  @Override // Record
127  public final T[] value() {
128    return this.value.clone();
129  }
130
131
132  /*
133   * Static methods.
134   */
135
136
137  /**
138   * Returns an {@link ArrayValue} suitable for the supplied argument.
139   *
140   * @param values a non-{@code null} array of values
141   *
142   * @return a non-{@code null} {@link ArrayValue}
143   *
144   * @exception NullPointerException if {@code values} is {@code null}
145   *
146   * @see #of(Value[])
147   */
148  public static final ArrayValue<BooleanValue> of(final boolean... values) {
149    final var va = new BooleanValue[values.length];
150    for (int i = 0; i < values.length; i++) {
151      va[i] = BooleanValue.of(values[i]);
152    }
153    return new ArrayValue<>(va);
154  }
155
156  /**
157   * Returns an {@link ArrayValue} suitable for the supplied argument.
158   *
159   * @param values a non-{@code null} array of values
160   *
161   * @return a non-{@code null} {@link ArrayValue}
162   *
163   * @exception NullPointerException if {@code values} is {@code null}
164   *
165   * @see #of(Value[])
166   */
167  public static final ArrayValue<ByteValue> of(final byte... values) {
168    final var va = new ByteValue[values.length];
169    for (int i = 0; i < values.length; i++) {
170      va[i] = ByteValue.of(values[i]);
171    }
172    return new ArrayValue<>(va);
173  }
174
175  /**
176   * Returns an {@link ArrayValue} suitable for the supplied argument.
177   *
178   * @param values a non-{@code null} array of values
179   *
180   * @return a non-{@code null} {@link ArrayValue}
181   *
182   * @exception NullPointerException if {@code values} is {@code null}
183   *
184   * @see #of(Value[])
185   */
186  public static final ArrayValue<CharValue> of(final char... values) {
187    final var va = new CharValue[values.length];
188    for (int i = 0; i < values.length; i++) {
189      va[i] = CharValue.of(values[i]);
190    }
191    return new ArrayValue<>(va);
192  }
193
194  /**
195   * Returns an {@link ArrayValue} suitable for the supplied argument.
196   *
197   * @param values a non-{@code null} array of values
198   *
199   * @return a non-{@code null} {@link ArrayValue}
200   *
201   * @exception NullPointerException if {@code values} is {@code null}
202   *
203   * @see #of(Value[])
204   */
205  public static final ArrayValue<DoubleValue> of(final double... values) {
206    final var va = new DoubleValue[values.length];
207    for (int i = 0; i < values.length; i++) {
208      va[i] = DoubleValue.of(values[i]);
209    }
210    return new ArrayValue<>(va);
211  }
212
213  /**
214   * Returns an {@link ArrayValue} suitable for the supplied argument.
215   *
216   * @param values a non-{@code null} array of values
217   *
218   * @return a non-{@code null} {@link ArrayValue}
219   *
220   * @exception NullPointerException if {@code values} is {@code null}
221   *
222   * @see #of(Value[])
223   */
224  public static final ArrayValue<FloatValue> of(final float... values) {
225    final var va = new FloatValue[values.length];
226    for (int i = 0; i < values.length; i++) {
227      va[i] = FloatValue.of(values[i]);
228    }
229    return new ArrayValue<>(va);
230  }
231
232  /**
233   * Returns an {@link ArrayValue} suitable for the supplied argument.
234   *
235   * @param values a non-{@code null} array of values
236   *
237   * @return a non-{@code null} {@link ArrayValue}
238   *
239   * @exception NullPointerException if {@code values} is {@code null}
240   *
241   * @see #of(Value[])
242   */
243  public static final ArrayValue<IntValue> of(final int... values) {
244    final var va = new IntValue[values.length];
245    for (int i = 0; i < values.length; i++) {
246      va[i] = IntValue.of(values[i]);
247    }
248    return new ArrayValue<>(va);
249  }
250
251  /**
252   * Returns an {@link ArrayValue} suitable for the supplied argument.
253   *
254   * @param values a non-{@code null} array of values
255   *
256   * @return a non-{@code null} {@link ArrayValue}
257   *
258   * @exception NullPointerException if {@code values} is {@code null}
259   *
260   * @see #of(Value[])
261   */
262  public static final ArrayValue<LongValue> of(final long... values) {
263    final var va = new LongValue[values.length];
264    for (int i = 0; i < values.length; i++) {
265      va[i] = LongValue.of(values[i]);
266    }
267    return new ArrayValue<>(va);
268  }
269
270  /**
271   * Returns an {@link ArrayValue} suitable for the supplied argument.
272   *
273   * @param values a non-{@code null} array of values
274   *
275   * @return a non-{@code null} {@link ArrayValue}
276   *
277   * @exception NullPointerException if {@code values} is {@code null}
278   *
279   * @see #of(Value[])
280   */
281  public static final ArrayValue<ShortValue> of(final short... values) {
282    final var va = new ShortValue[values.length];
283    for (int i = 0; i < values.length; i++) {
284      va[i] = ShortValue.of(values[i]);
285    }
286    return new ArrayValue<>(va);
287  }
288
289  /**
290   * Returns an {@link ArrayValue} suitable for the supplied argument.
291   *
292   * @param values a non-{@code null} array of values
293   *
294   * @return a non-{@code null} {@link ArrayValue}
295   *
296   * @exception NullPointerException if {@code values} is {@code null}
297   *
298   * @see #of(Value[])
299   */
300  public static final ArrayValue<ClassValue> of(final Class<?>... values) {
301    final var va = new ClassValue[values.length];
302    for (int i = 0; i < values.length; i++) {
303      va[i] = ClassValue.of(values[i]);
304    }
305    return new ArrayValue<>(va);
306  }
307
308  /**
309   * Returns an {@link ArrayValue} suitable for the supplied argument.
310   *
311   * @param values a non-{@code null} array of values
312   *
313   * @return a non-{@code null} {@link ArrayValue}
314   *
315   * @exception NullPointerException if {@code values} is {@code null}
316   *
317   * @see #of(Value[])
318   */
319  public static final ArrayValue<StringValue> of(final String... values) {
320    final var va = new StringValue[values.length];
321    for (int i = 0; i < values.length; i++) {
322      va[i] = StringValue.of(values[i]);
323    }
324    return new ArrayValue<>(va);
325  }
326
327  /**
328   * Returns an {@link ArrayValue} suitable for the supplied argument.
329   *
330   * @param <T> the type of {@link Value} held by the returned {@link ArrayValue}
331   *
332   * @param value a non-{@code null} {@link List} of suitably-typed {@link Value}s
333   *
334   * @return a non-{@code null} {@link ArrayValue}
335   *
336   * @exception NullPointerException if {@code value} is {@code null}
337   *
338   * @see #of(Value[])
339   */
340  @SuppressWarnings("unchecked")
341  // Called by #describeConstable()
342  public static final <T extends Value<T>> ArrayValue<T> of(final List<T> value) {
343    return of(value.toArray((T[])EMPTY_VALUE_ARRAY));
344  }
345
346  /**
347   * Returns an {@link ArrayValue} suitable for the supplied argument.
348   *
349   * @param <T> the type of {@link Value} held by the returned {@link ArrayValue}
350   *
351   * @param value a non-{@code null} array of suitably-typed {@link Value}s
352   *
353   * @return a non-{@code null} {@link ArrayValue}
354   *
355   * @exception NullPointerException if {@code value} is {@code null}
356   */
357  public static final <T extends Value<T>> ArrayValue<T> of(final T[] value) {
358    return new ArrayValue<>(value);
359  }
360
361}