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 (the
006 * "License"); you may not use this file except in compliance with the
007 * 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.header;
018
019
020import static org.apache.juneau.commons.utils.Shorts.*;
021import static org.apache.juneau.commons.utils.StringUtils.*;
022
023import java.util.*;
024import java.util.function.*;
025
026/**
027 * Base for simple comma-separated token lists.
028 *
029 * @since 9.2.1
030 */
031@SuppressWarnings({
032   "java:S2160", // equals() on HttpHeaderBean uses name + getValue(); typed state is reflected in getValue()
033   "java:S9149", // Public Juneau DSL factory; hiding parent of()/create() is intentional.
034   "unchecked" // Supplier<?> branches cast to typed suppliers after lazy-mode check
035})
036public class HttpCsvHeader extends HttpHeaderBean {
037
038   public static final int LAZY_WIRE_STRING = 0;
039   public static final int LAZY_TOKENS = 1;
040
041   private final String[] eagerTokens;
042   private final Supplier<?> lazySupplier;
043   private final int lazyMode;
044
045   /**
046    * Creates an {@link HttpCsvHeader} by parsing the given comma-delimited wire value.
047    *
048    * @param name Header name. Must not be {@code null}.
049    * @param wireValue Wire value. May be {@code null} or empty.
050    * @return A new instance. Never {@code null}.
051    */
052   public static HttpCsvHeader of(String name, String wireValue) {
053      return new HttpCsvHeader(name, wireValue);
054   }
055
056   /**
057    * Creates an {@link HttpCsvHeader} with the given typed values.
058    *
059    * @param name Header name. Must not be {@code null}.
060    * @param typedValues The token values. May be {@code null}.
061    * @return A new instance. Never {@code null}.
062    */
063   public static HttpCsvHeader of(String name, String...typedValues) {
064      return new HttpCsvHeader(name, typedValues);
065   }
066
067   /**
068    * Constructor.
069    *
070    * @param name Header name. Must not be <jk>null</jk>.
071    * @param value Wire value. Can be <jk>null</jk> or empty, in which case the parsed tokens are <jk>null</jk>.
072    */
073   protected HttpCsvHeader(String name, String value) {
074      super(name, value);
075      this.eagerTokens = null;
076      this.lazySupplier = null;
077      this.lazyMode = -1;
078   }
079
080   /**
081    * Constructor.
082    *
083    * @param name Header name. Must not be <jk>null</jk>.
084    * @param values The token values. Can be <jk>null</jk>, in which case the parsed tokens are <jk>null</jk>.
085    */
086   protected HttpCsvHeader(String name, String... values) {
087      super(name, values == null || values.length == 0 ? null : join(values, ", "));
088      this.eagerTokens = values == null ? null : cp(values);
089      this.lazySupplier = null;
090      this.lazyMode = -1;
091   }
092
093   /**
094    * Constructor with lazy value supplier.
095    *
096    * @param name Header name. Must not be <jk>null</jk>.
097    * @param supplier The lazy value supplier. Must not be <jk>null</jk>.
098    * @param lazyMode Either {@link #LAZY_WIRE_STRING} or {@link #LAZY_TOKENS}.
099    */
100   protected HttpCsvHeader(String name, Supplier<?> supplier, int lazyMode) {
101      super(name, lazyMode == LAZY_WIRE_STRING
102         ? ((Supplier<String>) supplier)::get
103         : () -> {
104            var t = ((Supplier<String[]>) supplier).get();
105            if (t == null)
106               return null;
107            return join(t, ", ");
108         });
109      this.eagerTokens = null;
110      this.lazySupplier = supplier;
111      this.lazyMode = lazyMode;
112   }
113
114   public Optional<String[]> asArray() {
115      return o(cp(csvTokens()));
116   }
117
118   public Optional<List<String>> asList() {
119      return o(u(l(csvTokens())));
120   }
121
122   public boolean contains(String val) {
123      var t = csvTokens();
124      if (t == null)
125         return false;
126      for (var v : t)
127         if (eq(v, val))
128            return true;
129      return false;
130   }
131
132   public boolean containsIgnoreCase(String val) {
133      var t = csvTokens();
134      if (t == null)
135         return false;
136      for (var v : t)
137         if (eqic(v, val))
138            return true;
139      return false;
140   }
141
142   /**
143    * Returns the comma-delimited wire-format value of this header.
144    *
145    * @return The wire value, or <jk>null</jk> if the value is unset.
146    */
147   @Override
148   public String getValue() {
149      var t = csvTokens();
150      return t == null ? null : join(t, ", ");
151   }
152
153   /**
154    * Returns the parsed tokens of this header, or the specified default if unset.
155    *
156    * @param other The default value. Can be <jk>null</jk> to allow a <jk>null</jk> result when the header is unset.
157    * @return The parsed tokens, or <c>other</c> if the value is unset. Can be <jk>null</jk> if <c>other</c> is <jk>null</jk>.
158    */
159   public String[] orElse(String[] other) {
160      var x = csvTokens();
161      return nn(x) ? cp(x) : other;
162   }
163
164   /**
165    * Returns the parsed tokens of this header.
166    *
167    * @return The parsed tokens, or <jk>null</jk> if the value is unset.
168    */
169   public String[] toArray() {
170      return cp(csvTokens());
171   }
172
173   public List<String> toList() {
174      return u(l(csvTokens()));
175   }
176
177   private String[] csvTokens() {
178      if (lazyMode == LAZY_TOKENS)
179         return ((Supplier<String[]>) lazySupplier).get();
180      if (eagerTokens != null)
181         return eagerTokens;
182      return splita(super.getValue());
183   }
184}