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