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.bean.jsonapi;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021
022import java.util.*;
023
024import org.apache.juneau.commons.bean.*;
025import org.apache.juneau.marshall.*;
026import org.apache.juneau.marshall.marshaller.*;
027
028/**
029 * Represents a JSON:API Resource Object as defined by
030 * <a href="https://jsonapi.org/format/#document-resource-objects">JSON:API v1.1 &#167; Resource Objects</a>.
031 *
032 * <p>
033 * Carries the standard six members: {@code type}, {@code id}, {@code attributes}, {@code relationships},
034 * {@code links}, {@code meta}.
035 *
036 * <h5 class='topic'>Why {@code type} is a plain String</h5>
037 *
038 * <p>
039 * JSON:API uses {@code type} as an open-ended string field on resource objects (the entity-type name, e.g.
040 * {@code "articles"}); the value is chosen by the API and is <b>not</b> a closed Java class hierarchy.
041 * Juneau's {@code @Marshalled(typePropertyName=...)} discriminator defaults to {@code _type} (so there is also
042 * no literal name clash). <b>Do NOT</b> annotate {@code JsonApiResource} with
043 * {@code @Marshalled(typePropertyName="type", dictionary=...)} — that would conflate JSON:API resource typing
044 * with Juneau bean dispatch, break round-tripping of unknown {@code type} values, and force every API to
045 * enumerate its resource types in a Java dictionary up front.
046 *
047 * <h5 class='section'>See Also:</h5><ul>
048 *   <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanJsonApi">juneau-bean-jsonapi</a>
049 * </ul>
050 */
051@Marshalled
052public class JsonApiResource {
053
054   private String type;
055   private String id;
056   private Map<String,Object> attributes;
057   private Map<String,JsonApiRelationship> relationships;
058   private Map<String,Object> links;
059   private Map<String,Object> meta;
060
061   /**
062    * Default constructor.
063    */
064   public JsonApiResource() {}
065
066   /**
067    * Convenience constructor.
068    *
069    * @param type The entity-type name.  Can be <jk>null</jk> to leave the property unset.
070    * @param id The identifier value.  Can be <jk>null</jk> to leave the property unset.
071    */
072   public JsonApiResource(String type, String id) {
073      this.type = type;
074      this.id = id;
075   }
076
077   /**
078    * Bean property getter:  <property>type</property>.
079    *
080    * <p>
081    * Plain {@code String} entity-type name. See class-level Javadoc for why this is not a Juneau polymorphic
082    * discriminator.
083    *
084    * @return The value of the <property>type</property> property, or <jk>null</jk> if it is not set.
085    */
086   public String getType() { return type; }
087
088   /**
089    * Bean property setter:  <property>type</property>.
090    *
091    * @param value The new value for this property.  Can be <jk>null</jk> to unset the property.
092    * @return This object.
093    */
094   public JsonApiResource setType(String value) {
095      type = value;
096      return this;
097   }
098
099   /**
100    * Bean property getter:  <property>id</property>.
101    *
102    * @return The value of the <property>id</property> property, or <jk>null</jk> if it is not set.
103    */
104   public String getId() { return id; }
105
106   /**
107    * Bean property setter:  <property>id</property>.
108    *
109    * @param value The new value for this property.  Can be <jk>null</jk> to unset the property.
110    * @return This object.
111    */
112   public JsonApiResource setId(String value) {
113      id = value;
114      return this;
115   }
116
117   /**
118    * Bean property getter:  <property>attributes</property>.
119    *
120    * <p>
121    * The spec lets {@code attributes} be any JSON object. Modeled as {@code Map&lt;String,Object&gt;} (a nested
122    * object on the wire — not flattened).
123    *
124    * @return The value of the <property>attributes</property> property, or <jk>null</jk> if it is not set.
125    */
126   public Map<String,Object> getAttributes() { return u(attributes); }
127
128   /**
129    * Bean property setter:  <property>attributes</property>.
130    *
131    * @param value The new value for this property.  Can be <jk>null</jk> to unset the property.
132    * @return This object.
133    */
134   public JsonApiResource setAttributes(Map<String,Object> value) {
135      attributes = value;
136      return this;
137   }
138
139   /**
140    * Convenience method to add a single attribute.
141    *
142    * @param name The attribute name.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
143    * @param value The attribute value.  Can be <jk>null</jk> (stored as <jk>null</jk>).
144    * @return This object.
145    */
146   public JsonApiResource putAttribute(String name, Object value) {
147      if (attributes == null)
148         attributes = map();
149      attributes.put(name, value);
150      return this;
151   }
152
153   /**
154    * Bean property getter:  <property>relationships</property>.
155    *
156    * @return The value of the <property>relationships</property> property, or <jk>null</jk> if it is not set.
157    */
158   public Map<String,JsonApiRelationship> getRelationships() { return u(relationships); }
159
160   /**
161    * Bean property setter:  <property>relationships</property>.
162    *
163    * @param value The new value for this property.  Can be <jk>null</jk> to unset the property.
164    * @return This object.
165    */
166   public JsonApiResource setRelationships(Map<String,JsonApiRelationship> value) {
167      relationships = value;
168      return this;
169   }
170
171   /**
172    * Convenience method to add a single named relationship.
173    *
174    * @param name The relationship name.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
175    * @param value The relationship object.  Can be <jk>null</jk> (stored as <jk>null</jk>).
176    * @return This object.
177    */
178   public JsonApiResource putRelationship(String name, JsonApiRelationship value) {
179      if (relationships == null)
180         relationships = map();
181      relationships.put(name, value);
182      return this;
183   }
184
185   /**
186    * Bean property getter:  <property>links</property>.
187    *
188    * <p>
189    * Per spec, each {@code links} value can be either a JSON string URL or a {@link JsonApiLink} object.
190    *
191    * @return The value of the <property>links</property> property, or <jk>null</jk> if it is not set.
192    */
193   @BeanProp(value="links")
194   @Swap(JsonApiLinkOrStringSwap.class)
195   public Map<String,Object> getLinks() { return u(links); }
196
197   /**
198    * Bean property setter:  <property>links</property>.
199    *
200    * @param value The new value for this property.  Can be <jk>null</jk> to unset the property.
201    * @return This object.
202    */
203   @BeanProp(value="links")
204   @Swap(JsonApiLinkOrStringSwap.class)
205   public JsonApiResource setLinks(Map<String,Object> value) {
206      links = value;
207      return this;
208   }
209
210   /**
211    * Convenience method to add a single link.
212    *
213    * @param name The link name.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
214    * @param value The link value (a {@link String} URL or a {@link JsonApiLink} object).  Can be <jk>null</jk> (stored as <jk>null</jk>).
215    * @return This object.
216    */
217   public JsonApiResource putLink(String name, Object value) {
218      if (links == null)
219         links = map();
220      links.put(name, value);
221      return this;
222   }
223
224   /**
225    * Bean property getter:  <property>meta</property>.
226    *
227    * @return The value of the <property>meta</property> property, or <jk>null</jk> if it is not set.
228    */
229   public Map<String,Object> getMeta() { return u(meta); }
230
231   /**
232    * Bean property setter:  <property>meta</property>.
233    *
234    * @param value The new value for this property.  Can be <jk>null</jk> to unset the property.
235    * @return This object.
236    */
237   public JsonApiResource setMeta(Map<String,Object> value) {
238      meta = value;
239      return this;
240   }
241
242   /**
243    * Convenience method to add a single meta entry.
244    *
245    * @param name The meta key.  Can be <jk>null</jk> ({@link LinkedHashMap} tolerates a <jk>null</jk> key).
246    * @param value The meta value.  Can be <jk>null</jk> (stored as <jk>null</jk>).
247    * @return This object.
248    */
249   public JsonApiResource putMeta(String name, Object value) {
250      if (meta == null)
251         meta = map();
252      meta.put(name, value);
253      return this;
254   }
255
256   @Override /* Overridden from Object */
257   public String toString() {
258      return Json.of(this);
259   }
260}