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;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.juneau.commons.httppart.*;
025import org.apache.juneau.commons.lang.*;
026import org.apache.juneau.commons.reflect.*;
027import org.apache.juneau.http.part.*;
028import org.apache.juneau.marshall.*;
029
030/**
031 * Static factory methods for creating HTTP parts (query parameters, form fields, path variables).
032 *
033 * <p>
034 * Import statically for clean DSL-style usage:
035 * <p class='bjava'>
036 *    import static HttpParts.*;
037 *
038 *    PartList <jv>form</jv> = PartList.<jsm>of</jsm>(
039 *       part(<js>"username"</js>, <js>"alice"</js>),
040 *       part(<js>"password"</js>, <jv>passwordSupplier</jv>)
041 *    );
042 * </p>
043 *
044 * <h5 class='section'>See Also:</h5><ul>
045 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/NextGenRestClient">Next-Generation REST Client</a>
046 * </ul>
047 *
048 * @since 9.2.1
049 */
050public final class HttpParts {
051
052   private HttpParts() {}
053
054   /**
055    * Creates an {@link HttpPart} with an eager string value.
056    *
057    * @param name The part name. Must not be <jk>null</jk>.
058    * @param value The part value. May be <jk>null</jk>.
059    * @return A new part. Never <jk>null</jk>.
060    */
061   public static HttpPart part(String name, String value) {
062      return HttpPartBean.of(name, value);
063   }
064
065   /**
066    * Creates an {@link HttpPart} with a lazy value supplier.
067    *
068    * @param name The part name. Must not be <jk>null</jk>.
069    * @param valueSupplier Supplier for the value. Must not be <jk>null</jk>.
070    * @return A new part. Never <jk>null</jk>.
071    */
072   public static HttpPart part(String name, Supplier<String> valueSupplier) {
073      return HttpPartBean.of(name, valueSupplier);
074   }
075
076   /**
077    * Creates a {@link PartList} from the given parts.
078    *
079    * @param parts The parts. Must not be <jk>null</jk>.
080    * @return A new list. Never <jk>null</jk>.
081    */
082   public static PartList partList(HttpPart... parts) {
083      return PartList.of(parts);
084   }
085
086   /**
087    * Creates a {@link PartList} from alternating name/value string pairs.
088    *
089    * @param pairs Alternating name/value strings. Must not be <jk>null</jk>. Length must be even.
090    * @return A new list. Never <jk>null</jk>.
091    */
092   public static PartList partListOfPairs(String... pairs) {
093      return PartList.ofPairs(pairs);
094   }
095
096   private static final Function<ClassMeta<?>,ConstructorInfo> CONSTRUCTOR_FUNCTION =
097      x -> x.getPublicConstructor(y -> y.hasParameterTypes(String.class))
098         .orElseGet(() -> x.getPublicConstructor(y -> y.hasParameterTypes(String.class, String.class)).orElse(null));
099
100   /**
101    * Returns the {@code (String)} or {@code (String,String)} public constructor for the specified type.
102    *
103    * <p>
104    * Used by part-parsing logic to instantiate typed parts from a wire string.
105    *
106    * @param type The type to find the constructor on. Must not be <jk>null</jk>.
107    * @return The constructor wrapped in an {@link Optional}; empty if none is found.
108    */
109   public static Optional<ConstructorInfo> getConstructor(ClassMeta<?> type) {
110      return type.getProperty("HttpPart.Constructor", CONSTRUCTOR_FUNCTION);
111   }
112
113   /**
114    * Returns <jk>true</jk> if the specified type is a transport-neutral part type for the given
115    * {@link HttpPartType}.
116    *
117    * <p>
118    * The check is whether the type is assignable to {@link HttpHeader} (for {@code HEADER}) or
119    * {@link HttpPart} (for {@code PATH}, {@code QUERY}, {@code FORMDATA}).
120    *
121    * @param partType The part type. Must not be <jk>null</jk>.
122    * @param type The type to check. Must not be <jk>null</jk>.
123    * @return <jk>true</jk> if the type is a part type for the given category.
124    */
125   public static boolean isHttpPart(HttpPartType partType, ClassMeta<?> type) {
126      return switch (partType) {
127         case PATH, QUERY, FORMDATA -> type.getProperty("HttpPart.isHttpPart", x -> x.isAssignableTo(HttpPart.class)).orElse(false);
128         case HEADER -> type.getProperty("HttpPart.isHttpHeader", x -> x.isAssignableTo(HttpHeader.class)).orElse(false);
129         default -> false;
130      };
131   }
132
133   private static final Function<ClassMeta<?>,String> HEADER_NAME_FUNCTION = x -> {
134      var n = Holder.<String>empty();
135      x.forEachAnnotation(Header.class, y -> ine(y.value()), y -> n.set(y.value()));
136      x.forEachAnnotation(Header.class, y -> ine(y.name()), y -> n.set(y.name()));
137      if (n.isEmpty())
138         n.set(readPublicStaticStringField(x.inner(), "NAME"));
139      return n.orElse(null);
140   };
141
142   private static final Function<ClassMeta<?>,String> QUERY_NAME_FUNCTION = x -> {
143      var n = Holder.<String>empty();
144      x.forEachAnnotation(Query.class, y -> ine(y.value()), y -> n.set(y.value()));
145      x.forEachAnnotation(Query.class, y -> ine(y.name()), y -> n.set(y.name()));
146      if (n.isEmpty())
147         n.set(readPublicStaticStringField(x.inner(), "NAME"));
148      return n.orElse(null);
149   };
150
151   private static final Function<ClassMeta<?>,String> FORMDATA_NAME_FUNCTION = x -> {
152      var n = Holder.<String>empty();
153      x.forEachAnnotation(FormData.class, y -> ine(y.value()), y -> n.set(y.value()));
154      x.forEachAnnotation(FormData.class, y -> ine(y.name()), y -> n.set(y.name()));
155      if (n.isEmpty())
156         n.set(readPublicStaticStringField(x.inner(), "NAME"));
157      return n.orElse(null);
158   };
159
160   private static final Function<ClassMeta<?>,String> PATH_NAME_FUNCTION = x -> {
161      var n = Holder.<String>empty();
162      x.forEachAnnotation(Path.class, y -> ine(y.value()), y -> n.set(y.value()));
163      x.forEachAnnotation(Path.class, y -> ine(y.name()), y -> n.set(y.name()));
164      if (n.isEmpty())
165         n.set(readPublicStaticStringField(x.inner(), "NAME"));
166      return n.orElse(null);
167   };
168
169   private static String readPublicStaticStringField(Class<?> c, String fieldName) {
170      try {
171         var f = c.getField(fieldName);
172         if (java.lang.reflect.Modifier.isStatic(f.getModifiers()) && f.getType() == String.class) {
173            var v = f.get(null);
174            return v == null ? null : v.toString();
175         }
176      } catch (@SuppressWarnings("unused") ReflectiveOperationException ignored) {
177         // Field does not exist or is not accessible.
178      }
179      return null;
180   }
181
182   /**
183    * Returns the name of the specified part type by reading the {@code @Header}, {@code @Query},
184    * {@code @FormData}, or {@code @Path} annotation on the class.
185    *
186    * @param partType The part type. Must not be <jk>null</jk>.
187    * @param type The type to check. Must not be <jk>null</jk>.
188    * @return The part name, wrapped in an {@link Optional}.
189    */
190   public static Optional<String> getName(HttpPartType partType, ClassMeta<?> type) {
191      return switch (partType) {
192         case FORMDATA -> type.getProperty("HttpPart.formData.name", FORMDATA_NAME_FUNCTION);
193         case HEADER -> type.getProperty("HttpPart.header.name", HEADER_NAME_FUNCTION);
194         case PATH -> type.getProperty("HttpPart.path.name", PATH_NAME_FUNCTION);
195         case QUERY -> type.getProperty("HttpPart.query.name", QUERY_NAME_FUNCTION);
196         default -> oe();
197      };
198   }
199}