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.juneau.http.part;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020import static org.apache.juneau.commons.utils.StringUtils.*;
021
022import java.util.*;
023import java.util.function.*;
024
025/**
026 * An {@link org.apache.juneau.http.HttpPart} whose value is a comma-delimited list of strings.
027 *
028 * <p>
029 * Mirrors the semantics of {@code BasicCsvArrayPart} from {@code juneau-rest-common-classic} without
030 * the Apache HttpCore dependency.
031 *
032 * @since 10.0.0
033 */
034@SuppressWarnings({
035   "java:S2160" // Equality is fully determined by name + getValue() in HttpPartBean; the typed fields are derived from the wire value and add no state to compare
036})
037public class HttpCsvArrayPart extends HttpPartBean {
038
039   private static final String[] EMPTY = {};
040
041   /**
042    * Creates an {@link HttpCsvArrayPart} with the given name and array value.
043    *
044    * @param name The part name. Must not be <jk>null</jk>.
045    * @param value The array of values. May be <jk>null</jk>.
046    * @return A new instance. Never <jk>null</jk>.
047    */
048   public static HttpCsvArrayPart of(String name, String...value) {
049      return new HttpCsvArrayPart(name, value);
050   }
051
052   /**
053    * Creates an {@link HttpCsvArrayPart} by parsing the given comma-delimited wire value.
054    *
055    * @param name The part name. Must not be <jk>null</jk>.
056    * @param value The wire value. May be <jk>null</jk> or empty.
057    * @return A new instance. Never <jk>null</jk>.
058    */
059   public static HttpCsvArrayPart ofString(String name, String value) {
060      return new HttpCsvArrayPart(name, value);
061   }
062
063   /**
064    * Creates an {@link HttpCsvArrayPart} with a lazy array supplier.
065    *
066    * @param name The part name. Must not be <jk>null</jk>.
067    * @param supplier Supplier for the array value. Must not be <jk>null</jk>.
068    * @return A new instance. Never <jk>null</jk>.
069    */
070   public static HttpCsvArrayPart ofLazy(String name, Supplier<String[]> supplier) {
071      return new HttpCsvArrayPart(name, supplier);
072   }
073
074   private final String[] typedValue;
075   private final Supplier<String[]> typedSupplier;
076
077   /**
078    * Constructor accepting an array of values.
079    *
080    * @param name The part name. Must not be <jk>null</jk>.
081    * @param value The array of values. May be <jk>null</jk>.
082    */
083   public HttpCsvArrayPart(String name, String...value) {
084      super(name, value == null ? null : join(value, ','));
085      this.typedValue = cp(value);
086      this.typedSupplier = null;
087   }
088
089   /**
090    * Constructor accepting a comma-delimited wire string.
091    *
092    * @param name The part name. Must not be <jk>null</jk>.
093    * @param value The wire value. May be <jk>null</jk> or empty.
094    */
095   public HttpCsvArrayPart(String name, String value) {
096      super(name, value);
097      this.typedValue = splita(value);
098      this.typedSupplier = null;
099   }
100
101   /**
102    * Constructor accepting a lazy supplier of array values.
103    *
104    * @param name The part name. Must not be <jk>null</jk>.
105    * @param supplier Supplier for the array value. Must not be <jk>null</jk>.
106    */
107   public HttpCsvArrayPart(String name, Supplier<String[]> supplier) {
108      super(name, () -> {
109         var v = supplier.get();
110         return v == null ? null : join(v, ',');
111      });
112      this.typedValue = null;
113      this.typedSupplier = supplier;
114   }
115
116   /**
117    * Returns the value as a copy of the underlying array, wrapped in an {@link Optional}.
118    *
119    * @return The array value, wrapped in an {@link Optional}. Never <jk>null</jk>.
120    */
121   public Optional<String[]> asArray() {
122      return o(toArray());
123   }
124
125   /**
126    * Returns the value as an unmodifiable {@link List}, wrapped in an {@link Optional}.
127    *
128    * @return The list value, wrapped in an {@link Optional}. Never <jk>null</jk>.
129    */
130   public Optional<List<String>> asList() {
131      return o(toList());
132   }
133
134   /**
135    * Returns {@code true} if this part contains the given value (case-sensitive).
136    *
137    * @param val The value to check for.
138    * @return {@code true} if the value is present.
139    */
140   public boolean contains(String val) {
141      if (val == null)
142         return false;
143      var arr = value();
144      if (arr == null)
145         return false;
146      for (var v : arr)
147         if (eq(v, val))
148            return true;
149      return false;
150   }
151
152   /**
153    * Returns {@code true} if this part contains the given value (case-insensitive).
154    *
155    * @param val The value to check for.
156    * @return {@code true} if the value is present.
157    */
158   public boolean containsIgnoreCase(String val) {
159      if (val == null)
160         return false;
161      var arr = value();
162      if (arr == null)
163         return false;
164      for (var v : arr)
165         if (eqic(v, val))
166            return true;
167      return false;
168   }
169
170   /**
171    * Returns a copy of the underlying array, or {@code null} if not set.
172    *
173    * @return The array, possibly {@code null}.
174    */
175   public String[] toArray() {
176      return cp(value());
177   }
178
179   /**
180    * Returns the value as an unmodifiable {@link List}, or {@code null} if not set.
181    *
182    * @return The list, possibly {@code null}.
183    */
184   public List<String> toList() {
185      return u(l(value()));
186   }
187
188   /**
189    * Returns the value if present, otherwise returns {@code other}.
190    *
191    * @param other The default value.
192    * @return The value or {@code other}.
193    */
194   public String[] orElse(String[] other) {
195      var x = value();
196      return nn(x) ? cp(x) : other;
197   }
198
199   private String[] value() {
200      if (typedSupplier != null) {
201         var v = typedSupplier.get();
202         return v == null ? EMPTY : v;
203      }
204      return typedValue;
205   }
206}