001/*
002 * Copyright (c) 2010-2024 Mark Allen, Norbert Bartels.
003 *
004 * Permission is hereby granted, free of charge, to any person obtaining a copy
005 * of this software and associated documentation files (the "Software"), to deal
006 * in the Software without restriction, including without limitation the rights
007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
008 * copies of the Software, and to permit persons to whom the Software is
009 * furnished to do so, subject to the following conditions:
010 *
011 * The above copyright notice and this permission notice shall be included in
012 * all copies or substantial portions of the Software.
013 *
014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
020 * THE SOFTWARE.
021 */
022package com.restfb;
023
024import static com.restfb.util.StringUtils.isBlank;
025import static com.restfb.util.StringUtils.trimToEmpty;
026import static java.lang.String.format;
027
028import java.util.Locale;
029import java.util.Optional;
030
031import com.restfb.exception.FacebookJsonMappingException;
032
033/**
034 * Representation of a Facebook API request parameter.
035 * 
036 * @author <a href="http://restfb.com">Mark Allen</a>
037 */
038public final class Parameter {
039  /**
040   * Parameter name.
041   */
042  public final String name;
043
044  /**
045   * Parameter value.
046   */
047  public final String value;
048
049  /**
050   * Creates a new parameter with the given {@code name} and {@code value}.
051   * 
052   * @param name
053   *          The parameter name.
054   * @param value
055   *          The parameter value.
056   * @param jsonMapper
057   *          Mapper for converting the parameter value to JSON.
058   * @throws IllegalArgumentException
059   *           If {@code name} is {@code null} or a blank string or either {@code value} or {@code jsonMapper} is
060   *           {@code null}.
061   */
062  private Parameter(String name, Object value, JsonMapper jsonMapper) {
063    if (isBlank(name) || value == null) {
064      throw new IllegalArgumentException(Parameter.class + " instances must have a non-blank name and non-null value."
065          + " Got instead name:" + name + ", value:" + value);
066    }
067
068    this.value = Optional.ofNullable(jsonMapper)
069      .orElseThrow(() -> new IllegalArgumentException("Provided " + JsonMapper.class + " must not be null."))
070      .toJson(value, true);
071    this.name = trimToEmpty(name);
072  }
073
074  /**
075   * Factory method which provides an instance with the given {@code name} and {@code value}.
076   * <p>
077   * The {@code value} parameter is often a {@link String} or primitive type like {@link Integer}, but you may pass in a
078   * {@link java.util.List}, {@link java.util.Map}, or your own <tt>@Facebook</tt>-annotated Javabean, and it will be
079   * converted to JSON automatically. See the "attachment" section of
080   * <a href="http://wiki.developers.facebook.com/index.php/Stream.publish">the stream.publish API documentation</a> for
081   * an example of where this is useful.
082   * 
083   * @param name
084   *          The parameter name.
085   * @param value
086   *          The parameter value.
087   * @return A {@code Parameter} instance with the given {@code name} and {@code value}.
088   * @throws IllegalArgumentException
089   *           If {@code name} or {@code value} is {@code null} or a blank string.
090   * @throws FacebookJsonMappingException
091   *           If an error occurs when converting {@code value} to JSON.
092   */
093  public static Parameter with(String name, Object value) {
094    return Parameter.with(name, value, new DefaultJsonMapper());
095  }
096
097  /**
098   * convenience factory method which needs a comma separated list of fields that the dev likes to fetch from the API
099   * 
100   * @param fieldList
101   *          comma separated list of fields
102   * @return Parameter object
103   */
104  public static Parameter withFields(String fieldList) {
105    return Parameter.with("fields", fieldList);
106  }
107
108  /**
109   * Convenience factory method that creates a {@code Parameter} object to retrieve the metadata from the API.
110   *
111   * @return Parameter object
112   */
113  public static Parameter withMetadata() {
114    return Parameter.with("metadata", "1");
115  }
116
117  /**
118   * convenience factory method which creates a {@code Parameter} object to fetch data from API with given locale
119   *
120   * @param locale
121   *          the locale that should be used to fetch the data
122   *
123   * @return Parameter object
124   */
125  public static Parameter withLocale(Locale locale) {
126    return Parameter.with("locale", locale.getLanguage());
127  }
128
129  /**
130   * convenience factory method which creates a {@code Parameter} object to fetch data from API with given limit
131   *
132   * @param limit
133   *          the limit that should be used fetching data
134   *
135   * @return Parameter object
136   */
137  public static Parameter withLimit(int limit) {
138    return Parameter.with("limit", limit);
139  }
140
141  /**
142   * convenience factory method which creates a {@code Parameter} object to post an object with a message parameter to
143   * the API
144   *
145   * @param obj
146   *          The content of the message, should be a String, but maybe some JSON
147   *
148   * @return Parameter object
149   */
150  public static Parameter withMessage(Object obj) {
151    return Parameter.with("message", obj);
152  }
153
154  /**
155   * convenience factory method which creates a {@code Parameter} object to query some information e.g. searching profiles
156   * the API
157   *
158   * @param queryString
159   *          the String used in the query
160   *
161   * @return Parameter object
162   */
163  public static Parameter withQuery(String queryString) {
164    return Parameter.with("q", queryString);
165  }
166
167  /**
168   * Factory method which provides an instance with the given {@code name} and {@code value}, using the provided
169   * {@code jsonMapper} to turn {@code value} into a JSON string.
170   * <p>
171   * The {@code value} parameter is often a {@link String} or primitive type like {@link Integer}, but you may pass in a
172   * {@link java.util.List}, {@link java.util.Map}, or your own <tt>@Facebook</tt>-annotated Javabean, and it will be
173   * converted to JSON automatically. See the "attachment" section of
174   * <a href="http://wiki.developers.facebook.com/index.php/Stream.publish">the stream.publish API documentation</a> for
175   * an example of where this is useful.
176   * 
177   * @param name
178   *          The parameter name.
179   * @param value
180   *          The parameter value.
181   * @param jsonMapper
182   *          The jsonMapper
183   * @return A {@code Parameter} instance with the given {@code name} and {@code value}.
184   * @throws IllegalArgumentException
185   *           If {@code name} or {@code value} is {@code null} or a blank string.
186   * @throws FacebookJsonMappingException
187   *           If an error occurs when converting {@code value} to JSON.
188   */
189  public static Parameter with(String name, Object value, JsonMapper jsonMapper) {
190    return new Parameter(name, value, jsonMapper);
191  }
192
193  /**
194   * @see java.lang.Object#equals(java.lang.Object)
195   */
196  @Override
197  public boolean equals(Object obj) {
198    if (obj == null) {
199      return false;
200    }
201    if (!getClass().equals(obj.getClass())) {
202      return false;
203    }
204
205    Parameter other = (Parameter) obj;
206
207    if (this.name != other.name && !this.name.equals(other.name)) {
208      return false;
209    }
210
211    return !(this.value != other.value && !this.value.equals(other.value));
212  }
213
214  @Override
215  public int hashCode() {
216    int hash = 7;
217    hash = 37 * hash + this.name.hashCode();
218    hash = 41 * hash + this.value.hashCode();
219    return hash;
220  }
221
222  @Override
223  public String toString() {
224    return format("Parameter[%s=%s]", name, value);
225  }
226}