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.classic.part;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.http.*;
025import org.apache.juneau.commons.reflect.*;
026import org.apache.juneau.http.classic.header.*;
027import org.apache.juneau.marshall.*;
028import org.apache.juneau.bct.assertions.*;
029
030/**
031 * Implementation of {@link NameValuePair} for serializing POJOs as URL-encoded form post entries.
032 *
033 * Provides the following features:
034 * <ul class='spaced-list'>
035 *    <li>
036 *       Values from {@link Supplier Suppliers}.
037 *    <li>
038 *       Caching.
039 *    <li>
040 *       Fluent setters.
041 *    <li>
042 *       Fluent assertions.
043 * </ul>
044 *
045 * <h5 class='section'>See Also:</h5><ul>
046 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a>
047 * </ul>
048 */
049@Marshalled(as=MarshalledAs.STRING)
050@SuppressWarnings({
051   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
052   "java:S3740" // Raw Supplier/Object types used where HTTP part value type cannot be parameterized at this abstraction level
053})
054public class BasicPart implements NameValuePair, Headerable {
055
056   /**
057    * Returns <jk>true</jk> if the {@link #cast(Object)} method can be used on the specified object.
058    *
059    * @param o The object to check.  Can be <jk>null</jk> (returns <jk>false</jk>).
060    * @return <jk>true</jk> if the {@link #cast(Object)} method can be used on the specified object.
061    */
062   public static boolean canCast(Object o) {
063      if (o == null)
064         return false;
065      var ci = ClassInfo.of(o);
066      return nn(ci) && ci.isAssignableToAny(Headerable.class, NameValuePair.class, NameValuePairable.class, Map.Entry.class);
067   }
068
069   /**
070    * Utility method for converting an arbitrary object to a {@link NameValuePair}.
071    *
072    * @param o
073    *    The object to cast or convert to a {@link NameValuePair}.
074    * @return Either the same object cast as a {@link NameValuePair} or converted to a {@link NameValuePair}.
075    */
076   public static NameValuePair cast(Object o) {
077      if (o instanceof NameValuePair o2)
078         return o2;
079      if (o instanceof NameValuePairable o2)
080         return o2.asNameValuePair();
081      if (o instanceof Headerable o2) {
082         var x = o2.asHeader();
083         return BasicPart.of(x.getName(), x.getValue());
084      }
085      if (o instanceof Map.Entry e) {
086         return BasicPart.of(s(e.getKey()), e.getValue());
087      }
088      throw rex("Object of type %s could not be converted to a Part.", cn(o));
089   }
090
091   /**
092    * Static creator.
093    *
094    * @param name The part name.
095    * @param value The part value.
096    * @return A new {@link BasicPart} object.
097    */
098   public static BasicPart of(String name, Object value) {
099      return new BasicPart(name, value);
100   }
101
102   /**
103    * Creates a {@link NameValuePair} from a name/value pair string (e.g. <js>"Foo: bar"</js>)
104    *
105    * @param pair The pair string.  Can be <jk>null</jk> (returns <jk>null</jk>).
106    * @return A new {@link NameValuePair} object, or <jk>null</jk> if the input was <jk>null</jk>.
107    */
108   public static BasicPart ofPair(String pair) {
109      if (pair == null)
110         return null;
111      var i = pair.indexOf(':');
112      if (i == -1)
113         i = pair.indexOf('=');
114      if (i == -1)
115         return of(pair, "");
116      return of(pair.substring(0, i).trim(), pair.substring(i + 1).trim());
117   }
118
119   private final String name;
120
121   private final Object value;
122
123   /**
124    * Constructor.
125    *
126    * @param name The part name.
127    * @param value The POJO to serialize to The part value.
128    */
129   public BasicPart(String name, Object value) {
130      this.name = name;
131      this.value = value;
132   }
133
134   /**
135    * Copy constructor.
136    *
137    * @param copyFrom The object to copy.  Must not be <jk>null</jk>.
138    */
139   protected BasicPart(BasicPart copyFrom) {
140      reqnn("copyFrom", copyFrom);
141      this.name = copyFrom.name;
142      this.value = copyFrom.value;
143   }
144
145   @Override /* Overridden from Headerable */
146   public BasicHeader asHeader() {
147      return BasicHeader.of(name, s(value));
148   }
149
150   /**
151    * Provides an object for performing assertions against the name of this pair.
152    *
153    * @return An object for performing assertions against the name of this pair.
154    */
155   public FluentStringAssertion<BasicPart> assertName() {
156      return new FluentStringAssertion<>(getName(), this);
157   }
158
159   /**
160    * Provides an object for performing assertions against the value of this pair.
161    *
162    * @return An object for performing assertions against the value of this pair.
163    */
164   public FluentStringAssertion<BasicPart> assertValue() {
165      return new FluentStringAssertion<>(getValue(), this);
166   }
167
168   @Override /* Overridden from NameValuePair */
169   public String getName() { return name; }
170
171   /**
172    * Returns the raw value of the part.
173    *
174    * @return The raw value of the part.
175    */
176   public Object getRawValue() { return unwrap(value); }
177
178   @Override /* Overridden from NameValuePair */
179   public String getValue() { return s(unwrap(value)); }
180
181   @Override
182   public boolean equals(Object o) {
183      return o instanceof NameValuePair nvp && eq(this, nvp, (x, y) -> eq(x.getName(), y.getName()) && eq(x.getValue(), y.getValue()));
184   }
185
186   @Override
187   public int hashCode() {
188      return h(name, getValue());
189   }
190
191   @Override /* Overridden from Object */
192   public String toString() {
193      return getName() + "=" + getValue();
194   }
195
196   private static Object unwrap(Object o) {
197      while (o instanceof Supplier)
198         o = ((Supplier<?>)o).get();
199      return o;
200   }
201}