001package com.box.sdk;
002
003import com.eclipsesource.json.JsonObject;
004import java.lang.reflect.Constructor;
005import java.lang.reflect.InvocationTargetException;
006import java.util.Collections;
007import java.util.Map;
008import java.util.concurrent.ConcurrentHashMap;
009
010/**
011 * The abstract base class for all resource types (files, folders, comments, collaborations, etc.)
012 * used by the API.
013 *
014 * <p>Every API resource has an ID and a {@link BoxAPIConnection} that it uses to communicate with
015 * the API. Some resources also have an associated {@link Info} class that contains information
016 * about the resource.
017 */
018public abstract class BoxResource {
019
020  /** @see #initResourceClassByType() */
021  private static final Map<String, Class<? extends BoxResource>> RESOURCE_CLASS_BY_TYPE =
022      initResourceClassByType();
023
024  private final BoxAPIConnection api;
025  private final String id;
026
027  /**
028   * Constructs a BoxResource for a resource with a given ID.
029   *
030   * @param api the API connection to be used by the resource.
031   * @param id the ID of the resource.
032   */
033  public BoxResource(BoxAPIConnection api, String id) {
034    this.api = api;
035    this.id = id;
036  }
037
038  /**
039   * @return Builds {@link Map} between String {@link #getResourceType(Class)} and {@link
040   *     BoxResource} type.
041   */
042  private static Map<String, Class<? extends BoxResource>> initResourceClassByType() {
043    Map<String, Class<? extends BoxResource>> result =
044        new ConcurrentHashMap<String, Class<? extends BoxResource>>();
045    result.put(getResourceType(BoxFolder.class), BoxFolder.class);
046    result.put(getResourceType(BoxFile.class), BoxFile.class);
047    result.put(getResourceType(BoxComment.class), BoxComment.class);
048    result.put(getResourceType(BoxCollaboration.class), BoxCollaboration.class);
049    result.put(getResourceType(BoxTask.class), BoxTask.class);
050    result.put(getResourceType(BoxTaskAssignment.class), BoxTaskAssignment.class);
051    result.put(getResourceType(BoxUser.class), BoxUser.class);
052    result.put(getResourceType(BoxGroup.class), BoxGroup.class);
053    result.put(getResourceType(BoxGroupMembership.class), BoxGroupMembership.class);
054    result.put(getResourceType(BoxEvent.class), BoxEvent.class);
055    result.put(getResourceType(BoxWebHook.class), BoxWebHook.class);
056    result.put(getResourceType(BoxCollection.class), BoxCollection.class);
057    result.put(getResourceType(BoxDevicePin.class), BoxDevicePin.class);
058    result.put(getResourceType(BoxRetentionPolicy.class), BoxRetentionPolicy.class);
059    result.put(
060        getResourceType(BoxRetentionPolicyAssignment.class), BoxRetentionPolicyAssignment.class);
061    result.put(getResourceType(BoxFileVersionRetention.class), BoxFileVersionRetention.class);
062    result.put(getResourceType(BoxLegalHoldPolicy.class), BoxLegalHoldPolicy.class);
063    result.put(getResourceType(BoxLegalHoldAssignment.class), BoxLegalHoldAssignment.class);
064    result.put(getResourceType(BoxFileVersionLegalHold.class), BoxFileVersionLegalHold.class);
065    result.put(getResourceType(BoxFileUploadSession.class), BoxFileUploadSession.class);
066    result.put(getResourceType(BoxWebLink.class), BoxWebLink.class);
067    result.put(getResourceType(BoxStoragePolicy.class), BoxStoragePolicy.class);
068    result.put(getResourceType(BoxStoragePolicyAssignment.class), BoxStoragePolicyAssignment.class);
069    result.put(getResourceType(BoxFolderLock.class), BoxFolderLock.class);
070    result.put(getResourceType(BoxFileRequest.class), BoxFileRequest.class);
071
072    return Collections.unmodifiableMap(result);
073  }
074
075  /**
076   * Resolves {@link BoxResourceType} for a provided {@link BoxResource} {@link Class}.
077   *
078   * @param clazz {@link BoxResource} type
079   * @return resolved {@link BoxResourceType#value()}
080   */
081  public static String getResourceType(Class<? extends BoxResource> clazz) {
082    BoxResourceType resource = clazz.getAnnotation(BoxResourceType.class);
083    if (resource == null) {
084      throw new IllegalArgumentException(
085          "Provided BoxResource type does not have @BoxResourceType annotation.");
086    }
087    return resource.value();
088  }
089
090  static BoxResource.Info parseInfo(BoxAPIConnection api, JsonObject jsonObject) {
091    String type = jsonObject.get("type").asString();
092    String id = jsonObject.get("id").asString();
093
094    try {
095      Class<? extends BoxResource> resourceClass = RESOURCE_CLASS_BY_TYPE.get(type);
096      Constructor<? extends BoxResource> resourceConstructor =
097          resourceClass.getConstructor(BoxAPIConnection.class, String.class);
098
099      Class<?> infoClass =
100          resourceClass.getClassLoader().loadClass(resourceClass.getCanonicalName() + "$Info");
101      Constructor<?> infoConstructor =
102          infoClass.getDeclaredConstructor(resourceClass, JsonObject.class);
103
104      BoxResource resource = resourceConstructor.newInstance(api, id);
105      return (BoxResource.Info) infoConstructor.newInstance(resource, jsonObject);
106
107    } catch (ClassNotFoundException e) {
108      return null;
109    } catch (NoSuchMethodException e) {
110      return null;
111    } catch (IllegalAccessException e) {
112      throw new BoxAPIException("Can not create BoxResource.Info instance:", e);
113    } catch (InvocationTargetException e) {
114      throw new BoxAPIException("Can not create BoxResource.Info instance:", e);
115    } catch (InstantiationException e) {
116      throw new BoxAPIException("Can not create BoxResource.Info instance:", e);
117    }
118  }
119
120  /**
121   * Gets the API connection used by this resource.
122   *
123   * @return the API connection used by this resource.
124   */
125  public BoxAPIConnection getAPI() {
126    return this.api;
127  }
128
129  /**
130   * Gets the ID of this resource.
131   *
132   * @return the ID of this resource.
133   */
134  public String getID() {
135    return this.id;
136  }
137
138  /**
139   * Indicates whether this BoxResource is equal to another BoxResource. Two BoxResources are equal
140   * if they have the same type and ID.
141   *
142   * @param other the other BoxResource to compare.
143   * @return true if the type and IDs of the two resources are equal; otherwise false.
144   */
145  @Override
146  public boolean equals(Object other) {
147    if (other == null) {
148      return false;
149    }
150
151    if (this.getClass().equals(other.getClass())) {
152      BoxResource otherResource = (BoxResource) other;
153      return this.getID().equals(otherResource.getID());
154    }
155
156    return false;
157  }
158
159  /**
160   * Returns a hash code value for this BoxResource.
161   *
162   * @return a hash code value for this BoxResource.
163   */
164  @Override
165  public int hashCode() {
166    return this.getID().hashCode();
167  }
168
169  /** Contains information about a BoxResource. */
170  public abstract class Info extends BoxJSONObject {
171    /** Constructs an empty Info object. */
172    public Info() {
173      super();
174    }
175
176    /**
177     * Constructs an Info object by parsing information from a JSON string.
178     *
179     * @param json the JSON string to parse.
180     */
181    public Info(String json) {
182      super(json);
183    }
184
185    /**
186     * Constructs an Info object using an already parsed JSON object.
187     *
188     * @param jsonObject the parsed JSON object.
189     */
190    Info(JsonObject jsonObject) {
191      super(jsonObject);
192    }
193
194    /**
195     * Gets the ID of the resource associated with this Info.
196     *
197     * @return the ID of the associated resource.
198     */
199    public String getID() {
200      return BoxResource.this.getID();
201    }
202
203    /**
204     * Gets the resource associated with this Info.
205     *
206     * @return the associated resource.
207     */
208    public abstract BoxResource getResource();
209  }
210}