001package com.box.sdk;
002
003import static com.box.sdk.http.HttpMethod.DELETE;
004
005import com.eclipsesource.json.Json;
006import com.eclipsesource.json.JsonArray;
007import com.eclipsesource.json.JsonObject;
008import com.eclipsesource.json.JsonValue;
009import java.io.File;
010import java.io.FileInputStream;
011import java.io.FileNotFoundException;
012import java.io.InputStream;
013import java.net.URL;
014import java.util.ArrayList;
015import java.util.Collection;
016import java.util.HashMap;
017import java.util.List;
018import java.util.Map;
019
020/**
021 * Represents a Box user account.
022 *
023 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked
024 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error
025 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p>
026 */
027@BoxResourceType("user")
028public class BoxUser extends BoxCollaborator {
029
030    /**
031     * An array of all possible file fields that can be requested when calling {@link #getInfo(String...)}.
032     */
033    public static final String[] ALL_FIELDS = {"type", "id", "name", "login", "created_at", "modified_at", "role",
034        "language", "timezone", "space_amount", "space_used", "max_upload_size", "tracking_codes",
035        "can_see_managed_users", "is_sync_enabled", "is_external_collab_restricted", "status", "job_title", "phone",
036        "address", "avatar_url", "is_exempt_from_device_limits", "is_exempt_from_login_verification", "enterprise",
037        "my_tags", "hostname", "is_platform_access_only", "external_app_user_id"};
038
039    /**
040     * User URL Template.
041     */
042    public static final URLTemplate USER_URL_TEMPLATE = new URLTemplate("users/%s");
043    /**
044     * Get Me URL Template.
045     */
046    public static final URLTemplate GET_ME_URL = new URLTemplate("users/me");
047    /**
048     * Users URL Template.
049     */
050    public static final URLTemplate USERS_URL_TEMPLATE = new URLTemplate("users");
051    /**
052     * User Memberships URL Template.
053     */
054    public static final URLTemplate USER_MEMBERSHIPS_URL_TEMPLATE = new URLTemplate("users/%s/memberships");
055    /**
056     * E-Mail Alias URL Template.
057     */
058    public static final URLTemplate EMAIL_ALIAS_URL_TEMPLATE = new URLTemplate("users/%s/email_aliases/%s");
059    /**
060     * E-Mail Aliases URL Template.
061     */
062    public static final URLTemplate EMAIL_ALIASES_URL_TEMPLATE = new URLTemplate("users/%s/email_aliases");
063    /**
064     * Move Folder To User Template.
065     */
066    public static final URLTemplate MOVE_FOLDER_TO_USER_TEMPLATE = new URLTemplate("users/%s/folders/%s");
067    /**
068     * User Avatar Template.
069     */
070    public static final URLTemplate USER_AVATAR_TEMPLATE = new URLTemplate("users/%s/avatar");
071
072    /**
073     * Constructs a BoxUser for a user with a given ID.
074     *
075     * @param api the API connection to be used by the user.
076     * @param id  the ID of the user.
077     */
078    public BoxUser(BoxAPIConnection api, String id) {
079        super(api, id);
080    }
081
082    /**
083     * Provisions a new app user in an enterprise using Box Developer Edition.
084     *
085     * @param api  the API connection to be used by the created user.
086     * @param name the name of the user.
087     * @return the created user's info.
088     */
089    public static BoxUser.Info createAppUser(BoxAPIConnection api, String name) {
090        return createAppUser(api, name, new CreateUserParams());
091    }
092
093    /**
094     * Provisions a new app user in an enterprise with additional user information using Box Developer Edition.
095     *
096     * @param api    the API connection to be used by the created user.
097     * @param name   the name of the user.
098     * @param params additional user information.
099     * @return the created user's info.
100     */
101    public static BoxUser.Info createAppUser(BoxAPIConnection api, String name,
102                                             CreateUserParams params) {
103
104        params.setIsPlatformAccessOnly(true);
105        return createEnterpriseUser(api, null, name, params);
106    }
107
108    /**
109     * Provisions a new user in an enterprise.
110     *
111     * @param api   the API connection to be used by the created user.
112     * @param login the email address the user will use to login.
113     * @param name  the name of the user.
114     * @return the created user's info.
115     */
116    public static BoxUser.Info createEnterpriseUser(BoxAPIConnection api, String login, String name) {
117        return createEnterpriseUser(api, login, name, null);
118    }
119
120    /**
121     * Provisions a new user in an enterprise with additional user information.
122     *
123     * @param api    the API connection to be used by the created user.
124     * @param login  the email address the user will use to login.
125     * @param name   the name of the user.
126     * @param params additional user information.
127     * @return the created user's info.
128     */
129    public static BoxUser.Info createEnterpriseUser(BoxAPIConnection api, String login, String name,
130                                                    CreateUserParams params) {
131
132        JsonObject requestJSON = new JsonObject();
133        requestJSON.add("login", login);
134        requestJSON.add("name", name);
135
136        if (params != null) {
137            if (params.getRole() != null) {
138                requestJSON.add("role", params.getRole().toJSONValue());
139            }
140
141            if (params.getStatus() != null) {
142                requestJSON.add("status", params.getStatus().toJSONValue());
143            }
144
145            requestJSON.add("language", params.getLanguage());
146            requestJSON.add("is_sync_enabled", params.getIsSyncEnabled());
147            requestJSON.add("job_title", params.getJobTitle());
148            requestJSON.add("phone", params.getPhone());
149            requestJSON.add("address", params.getAddress());
150            requestJSON.add("space_amount", params.getSpaceAmount());
151            requestJSON.add("can_see_managed_users", params.getCanSeeManagedUsers());
152            requestJSON.add("timezone", params.getTimezone());
153            requestJSON.add("is_exempt_from_device_limits", params.getIsExemptFromDeviceLimits());
154            requestJSON.add("is_exempt_from_login_verification", params.getIsExemptFromLoginVerification());
155            requestJSON.add("is_platform_access_only", params.getIsPlatformAccessOnly());
156            requestJSON.add("external_app_user_id", params.getExternalAppUserId());
157            requestJSON.add("is_external_collab_restricted", params.getIsExternalCollabRestricted());
158            if (params.getTrackingCodes() != null) {
159                requestJSON.add("tracking_codes", toTrackingCodesJson(params.getTrackingCodes()));
160            }
161        }
162
163        URL url = USERS_URL_TEMPLATE.build(api.getBaseURL());
164        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
165        request.setBody(requestJSON.toString());
166        BoxJSONResponse response = (BoxJSONResponse) request.send();
167        JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
168
169        BoxUser createdUser = new BoxUser(api, responseJSON.get("id").asString());
170        return createdUser.new Info(responseJSON);
171    }
172
173    /**
174     * Gets the current user.
175     *
176     * @param api the API connection of the current user.
177     * @return the current user.
178     */
179    public static BoxUser getCurrentUser(BoxAPIConnection api) {
180        URL url = GET_ME_URL.build(api.getBaseURL());
181        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
182        BoxJSONResponse response = (BoxJSONResponse) request.send();
183        JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
184        return new BoxUser(api, jsonObject.get("id").asString());
185    }
186
187    /**
188     * Returns an iterable containing all the enterprise users.
189     *
190     * @param api the API connection to be used when retrieving the users.
191     * @return an iterable containing all the enterprise users.
192     */
193    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(final BoxAPIConnection api) {
194        return getAllEnterpriseUsers(api, false, null);
195    }
196
197
198    /**
199     * Returns an iterable containing all the enterprise users. Uses marker based pagination.
200     *
201     * @param api       the API connection to be used when retrieving the users.
202     * @param usemarker Boolean that determines whether to use marker based pagination.
203     * @param marker    The marker at which the iterator will begin.
204     * @return an iterable containing all the enterprise users.
205     */
206    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(final BoxAPIConnection api, final boolean usemarker,
207                                                               final String marker) {
208        return getUsersInfoForType(api, null, null, null, usemarker, marker);
209    }
210
211    /**
212     * Returns an iterable containing all the enterprise users that matches the filter and specifies which child fields
213     * to retrieve from the API.
214     *
215     * @param api        the API connection to be used when retrieving the users.
216     * @param filterTerm used to filter the results to only users starting with this string in either the name or the
217     *                   login. Can be null to not filter the results.
218     * @param fields     the fields to retrieve. Leave this out for the standard fields.
219     * @return an iterable containing all the enterprise users that matches the filter.
220     */
221    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(final BoxAPIConnection api, final String filterTerm,
222                                                               final String... fields) {
223        return getUsersInfoForType(api, filterTerm, null, null, false, null, fields);
224    }
225
226    /**
227     * Returns an iterable containing all the enterprise users that matches the filter and specifies which child fields
228     * to retrieve from the API. Uses marker based pagination.
229     *
230     * @param api        the API connection to be used when retrieving the users.
231     * @param filterTerm used to filter the results to only users starting with this string in either the name or the
232     *                   login. Can be null to not filter the results.
233     * @param usemarker  Boolean that determines whether to use marker based pagination.
234     * @param marker     The marker at which the iterator will begin.
235     * @param fields     the fields to retrieve. Leave this out for the standard fields.
236     * @return an iterable containing all the enterprise users that matches the filter.
237     */
238    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(
239        final BoxAPIConnection api,
240        final String filterTerm,
241        final boolean usemarker,
242        final String marker,
243        final String... fields
244    ) {
245        return getUsersInfoForType(api, filterTerm, null, null, usemarker, marker, fields);
246    }
247
248    /**
249     * Gets a limited set of information about an external user. (A user collaborating
250     * on content owned by the enterprise). Note: Only fields the user has permission to
251     * see will be returned with values. Other fields will return a value of null.
252     *
253     * @param api        the API connection to be used when retrieving the users.
254     * @param filterTerm used to filter the results to only users matching the given login.
255     *                   This does exact match only, so if no filter term is passed in, nothing
256     *                   will be returned.
257     * @param fields     the fields to retrieve. Leave this out for the standard fields.
258     * @return an iterable containing external users matching the given email
259     */
260    public static Iterable<BoxUser.Info> getExternalUsers(final BoxAPIConnection api, final String filterTerm,
261                                                          final String... fields) {
262        return getUsersInfoForType(api, filterTerm, "external", null, false, null, fields);
263    }
264
265    /**
266     * Gets a limited set of information about an external user. (A user collaborating
267     * on content owned by the enterprise). Note: Only fields the user has permission to
268     * see will be returned with values. Other fields will return a value of null. Uses marker based pagination.
269     *
270     * @param api        the API connection to be used when retrieving the users.
271     * @param filterTerm used to filter the results to only users matching the given login.
272     *                   This does exact match only, so if no filter term is passed in, nothing
273     *                   will be returned.
274     * @param usemarker  Boolean that determines whether to use marker based pagination.
275     * @param marker     The marker at which the iterator will begin.
276     * @param fields     the fields to retrieve. Leave this out for the standard fields.
277     * @return an iterable containing external users matching the given email
278     */
279    public static Iterable<BoxUser.Info> getExternalUsers(
280        final BoxAPIConnection api,
281        final String filterTerm,
282        final boolean usemarker,
283        final String marker,
284        final String... fields
285    ) {
286        return getUsersInfoForType(api, filterTerm, "external", null, usemarker, marker, fields);
287    }
288
289    /**
290     * Gets any managed users that match the filter term as well as any external users that
291     * match the filter term. For managed users it matches any users names or emails that
292     * start with the term. For external, it only does full match on email. This method
293     * is ideal to use in the case where you have a full email for a user and you don't
294     * know if they're managed or external.
295     *
296     * @param api        the API connection to be used when retrieving the users.
297     * @param filterTerm The filter term to lookup users by (login for external, login or name for managed)
298     * @param fields     the fields to retrieve. Leave this out for the standard fields.
299     * @return an iterable containing users matching the given email
300     */
301    public static Iterable<BoxUser.Info> getAllEnterpriseOrExternalUsers(
302        final BoxAPIConnection api,
303        final String filterTerm,
304        final String... fields
305    ) {
306        return getUsersInfoForType(api, filterTerm, "all", null, false, null, fields);
307    }
308
309    /**
310     * Gets any managed users that match the filter term as well as any external users that
311     * match the filter term. For managed users it matches any users names or emails that
312     * start with the term. For external, it only does full match on email. This method
313     * is ideal to use in the case where you have a full email for a user and you don't
314     * know if they're managed or external. Uses marker based pagination.
315     *
316     * @param api        the API connection to be used when retrieving the users.
317     * @param filterTerm The filter term to lookup users by (login for external, login or name for managed)
318     * @param usemarker  Boolean that determines whether to use marker based pagination.
319     * @param marker     The marker at which the iterator will begin.
320     * @param fields     the fields to retrieve. Leave this out for the standard fields.
321     * @return an iterable containing users matching the given email
322     */
323    public static Iterable<BoxUser.Info> getAllEnterpriseOrExternalUsers(
324        final BoxAPIConnection api,
325        final String filterTerm,
326        final boolean usemarker,
327        final String marker,
328        final String... fields
329    ) {
330        return getUsersInfoForType(api, filterTerm, "all", null, usemarker, marker, fields);
331    }
332
333    /**
334     * Gets any app users that has an exact match with the externalAppUserId term.
335     *
336     * @param api               the API connection to be used when retrieving the users.
337     * @param externalAppUserId the external app user id that has been set for app user
338     * @param fields            the fields to retrieve. Leave this out for the standard fields.
339     * @return an iterable containing users matching the given email
340     */
341    public static Iterable<BoxUser.Info> getAppUsersByExternalAppUserID(
342        final BoxAPIConnection api,
343        final String externalAppUserId,
344        final String... fields
345    ) {
346        return getUsersInfoForType(api, null, null, externalAppUserId, false, null, fields);
347    }
348
349    /**
350     * Gets any app users that has an exact match with the externalAppUserId term using marker based pagination.
351     *
352     * @param api               the API connection to be used when retrieving the users.
353     * @param externalAppUserId the external app user id that has been set for app user
354     * @param usemarker         Boolean that determines whether to use marker based pagination.
355     * @param marker            The marker at which the iterator will begin.
356     * @param fields            the fields to retrieve. Leave this out for the standard fields.
357     * @return an iterable containing users matching the given email
358     */
359    public static Iterable<BoxUser.Info> getAppUsersByExternalAppUserID(
360        final BoxAPIConnection api,
361        final String externalAppUserId,
362        final boolean usemarker,
363        String marker,
364        final String... fields
365    ) {
366        return getUsersInfoForType(api, null, null, externalAppUserId, usemarker, marker, fields);
367    }
368
369    /**
370     * Helper method to abstract out the common logic from the various users methods.
371     *
372     * @param api               the API connection to be used when retrieving the users.
373     * @param filterTerm        The filter term to lookup users by (login for external, login or name for managed)
374     * @param userType          The type of users we want to search with this request.
375     *                          Valid values are 'managed' (enterprise users), 'external' or 'all'
376     * @param externalAppUserId the external app user id that has been set for an app user
377     * @param usemarker         Boolean that determines whether to use marker based pagination.
378     * @param marker            The marker at which the iterator will begin.
379     * @param fields            the fields to retrieve. Leave this out for the standard fields.
380     * @return An iterator over the selected users.
381     */
382    private static Iterable<BoxUser.Info> getUsersInfoForType(
383        final BoxAPIConnection api,
384        final String filterTerm,
385        final String userType,
386        final String externalAppUserId,
387        final boolean usemarker,
388        final String marker,
389        final String... fields
390    ) {
391
392        final QueryStringBuilder builder = new QueryStringBuilder();
393        if (filterTerm != null) {
394            builder.appendParam("filter_term", filterTerm);
395        }
396        if (userType != null) {
397            builder.appendParam("user_type", userType);
398        }
399        if (externalAppUserId != null) {
400            builder.appendParam("external_app_user_id", externalAppUserId);
401        }
402        if (usemarker) {
403            builder.appendParam("usemarker", "true");
404        }
405        if (fields.length > 0) {
406            builder.appendParam("fields", fields);
407        }
408        final URL url = USERS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString());
409
410        if (usemarker) {
411            return new BoxResourceIterable<BoxUser.Info>(api, url, 100, null, marker) {
412                @Override
413                protected BoxUser.Info factory(JsonObject jsonObject) {
414                    BoxUser user = new BoxUser(api, jsonObject.get("id").asString());
415                    return user.new Info(jsonObject);
416                }
417            };
418        } else {
419            return () -> new BoxUserIterator(api, url);
420        }
421    }
422
423    private static JsonArray toTrackingCodesJson(Map<String, String> trackingCodes) {
424        JsonArray trackingCodesJsonArray = new JsonArray();
425        for (String attrKey : trackingCodes.keySet()) {
426            JsonObject trackingCode = new JsonObject();
427            trackingCode.set("type", "tracking_code");
428            trackingCode.set("name", attrKey);
429            trackingCode.set("value", trackingCodes.get(attrKey));
430            trackingCodesJsonArray.add(trackingCode);
431        }
432        return trackingCodesJsonArray;
433    }
434
435    /**
436     * Gets information about this user.
437     *
438     * @param fields the optional fields to retrieve.
439     * @return info about this user.
440     */
441    public BoxUser.Info getInfo(String... fields) {
442        URL url;
443        if (fields.length > 0) {
444            String queryString = new QueryStringBuilder().appendParam("fields", fields).toString();
445            url = USER_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
446        } else {
447            url = USER_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
448        }
449        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
450        BoxJSONResponse response = (BoxJSONResponse) request.send();
451        JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
452        return new Info(jsonObject);
453    }
454
455    /**
456     * Gets information about all of the group memberships for this user.
457     * Does not support paging.
458     *
459     * <p>Note: This method is only available to enterprise admins.</p>
460     *
461     * @return a collection of information about the group memberships for this user.
462     */
463    public Collection<BoxGroupMembership.Info> getMemberships() {
464        BoxAPIConnection api = this.getAPI();
465        URL url = USER_MEMBERSHIPS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
466
467        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
468        BoxJSONResponse response = (BoxJSONResponse) request.send();
469        JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
470
471        int entriesCount = responseJSON.get("total_count").asInt();
472        Collection<BoxGroupMembership.Info> memberships = new ArrayList<>(entriesCount);
473        JsonArray entries = responseJSON.get("entries").asArray();
474        for (JsonValue entry : entries) {
475            JsonObject entryObject = entry.asObject();
476            BoxGroupMembership membership = new BoxGroupMembership(api, entryObject.get("id").asString());
477            BoxGroupMembership.Info info = membership.new Info(entryObject);
478            memberships.add(info);
479        }
480
481        return memberships;
482    }
483
484    /**
485     * Gets information about all of the group memberships for this user as iterable with paging support.
486     *
487     * @param fields the fields to retrieve.
488     * @return an iterable with information about the group memberships for this user.
489     */
490    public Iterable<BoxGroupMembership.Info> getAllMemberships(String... fields) {
491        final QueryStringBuilder builder = new QueryStringBuilder();
492        if (fields.length > 0) {
493            builder.appendParam("fields", fields);
494        }
495        return () -> {
496            URL url = USER_MEMBERSHIPS_URL_TEMPLATE.buildWithQuery(
497                BoxUser.this.getAPI().getBaseURL(), builder.toString(), BoxUser.this.getID());
498            return new BoxGroupMembershipIterator(BoxUser.this.getAPI(), url);
499        };
500    }
501
502    /**
503     * Adds a new email alias to this user's account.
504     *
505     * @param email the email address to add as an alias.
506     * @return the newly created email alias.
507     */
508    public EmailAlias addEmailAlias(String email) {
509        return this.addEmailAlias(email, false);
510    }
511
512    /**
513     * Adds a new email alias to this user's account and confirms it without user interaction.
514     * This functionality is only available for enterprise admins.
515     *
516     * @param email       the email address to add as an alias.
517     * @param isConfirmed whether or not the email alias should be automatically confirmed.
518     * @return the newly created email alias.
519     */
520    public EmailAlias addEmailAlias(String email, boolean isConfirmed) {
521        URL url = EMAIL_ALIASES_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
522        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
523
524        JsonObject requestJSON = new JsonObject()
525            .add("email", email);
526
527        if (isConfirmed) {
528            requestJSON.add("is_confirmed", isConfirmed);
529        }
530
531        request.setBody(requestJSON.toString());
532        BoxJSONResponse response = (BoxJSONResponse) request.send();
533        JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
534        return new EmailAlias(responseJSON);
535    }
536
537    /**
538     * Deletes an email alias from this user's account.
539     *
540     * <p>The IDs of the user's email aliases can be found by calling {@link #getEmailAliases}.</p>
541     *
542     * @param emailAliasID the ID of the email alias to delete.
543     */
544    public void deleteEmailAlias(String emailAliasID) {
545        URL url = EMAIL_ALIAS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), emailAliasID);
546        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
547        BoxAPIResponse response = request.send();
548        response.disconnect();
549    }
550
551    /**
552     * Gets a collection of all the email aliases for this user.
553     *
554     * <p>Note that the user's primary login email is not included in the collection of email aliases.</p>
555     *
556     * @return a collection of all the email aliases for this user.
557     */
558    public Collection<EmailAlias> getEmailAliases() {
559        URL url = EMAIL_ALIASES_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
560        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
561        BoxJSONResponse response = (BoxJSONResponse) request.send();
562        JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
563
564        int totalCount = responseJSON.get("total_count").asInt();
565        Collection<EmailAlias> emailAliases = new ArrayList<>(totalCount);
566        JsonArray entries = responseJSON.get("entries").asArray();
567        for (JsonValue value : entries) {
568            JsonObject emailAliasJSON = value.asObject();
569            emailAliases.add(new EmailAlias(emailAliasJSON));
570        }
571
572        return emailAliases;
573    }
574
575    /**
576     * Deletes a user from an enterprise account.
577     *
578     * @param notifyUser whether or not to send an email notification to the user that their account has been deleted.
579     * @param force      whether or not this user should be deleted even if they still own files.
580     */
581    public void delete(boolean notifyUser, boolean force) {
582        String queryString = new QueryStringBuilder()
583            .appendParam("notify", String.valueOf(notifyUser))
584            .appendParam("force", String.valueOf(force))
585            .toString();
586
587        URL url = USER_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
588        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
589        BoxAPIResponse response = request.send();
590        response.disconnect();
591    }
592
593    /**
594     * Updates the information about this user with any info fields that have been modified locally.
595     *
596     * <p>Note: This method is only available to enterprise admins.</p>
597     *
598     * @param info info the updated info.
599     */
600    public void updateInfo(BoxUser.Info info) {
601        URL url = USER_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
602        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
603        request.setBody(info.getPendingChanges());
604        BoxJSONResponse response = (BoxJSONResponse) request.send();
605        JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
606        info.update(jsonObject);
607    }
608
609    /**
610     * @param sourceUserID the user id of the user whose files will be the source for this operation
611     * @return info for the newly created folder
612     * @deprecated As of release 2.22.0, replaced by {@link #transferContent(String)} ()}
613     * <p>
614     * Moves all of the owned content from within one user’s folder into a new folder in another user's account.
615     * You can move folders across users as long as the you have administrative permissions and the 'source'
616     * user owns the folders. Per the documentation at the link below, this will move everything from the root
617     * folder, as this is currently the only mode of operation supported.
618     * <p>
619     * See also <a href="https://developer.box.com/en/reference/put-users-id-folders-id/">https://developer.box.com/en/reference/put-users-id-folders-id/</a>
620     */
621    @Deprecated
622    public BoxFolder.Info moveFolderToUser(String sourceUserID) {
623        // Currently the API only supports moving of the root folder (0), hence the hard coded "0"
624        URL url = MOVE_FOLDER_TO_USER_TEMPLATE.build(this.getAPI().getBaseURL(), sourceUserID, "0");
625        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
626        JsonObject idValue = new JsonObject();
627        idValue.add("id", this.getID());
628        JsonObject ownedBy = new JsonObject();
629        ownedBy.add("owned_by", idValue);
630        request.setBody(ownedBy.toString());
631        BoxJSONResponse response = (BoxJSONResponse) request.send();
632        JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
633        BoxFolder movedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
634
635        return movedFolder.new Info(responseJSON);
636    }
637
638    /**
639     * Moves all of the owned content from within one user’s folder into a new folder in another user's account.
640     * You can move folders across users as long as the you have administrative permissions and the 'source'
641     * user owns the folders. Per the documentation at the link below, this will move everything from the root
642     * folder, as this is currently the only mode of operation supported.
643     * <p>
644     * See also <a href="https://developer.box.com/en/reference/put-users-id-folders-id/">https://developer.box.com/en/reference/put-users-id-folders-id/</a>
645     *
646     * @param destinationUserID the user id of the user that you wish to transfer content to.
647     * @return info for the newly created folder.
648     */
649    public BoxFolder.Info transferContent(String destinationUserID) {
650        URL url = MOVE_FOLDER_TO_USER_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), "0");
651        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
652        JsonObject destinationUser = new JsonObject();
653        destinationUser.add("id", destinationUserID);
654        JsonObject ownedBy = new JsonObject();
655        ownedBy.add("owned_by", destinationUser);
656        request.setBody(ownedBy.toString());
657        BoxJSONResponse response = (BoxJSONResponse) request.send();
658        JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
659        BoxFolder movedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
660
661        return movedFolder.new Info(responseJSON);
662    }
663
664    /**
665     * Retrieves the avatar of a user as an InputStream.
666     *
667     * @return InputStream representing the user avater.
668     */
669    public InputStream getAvatar() {
670        URL url = USER_AVATAR_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
671        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
672        BoxAPIResponse response = request.send();
673
674        return response.getBody();
675    }
676
677    /**
678     * Upload avatar image to user account. Supported formats are JPG and PNG.
679     * The image size cannot exceed 1024 * 1024 pixels or 1MB.
680     *
681     * @param file File containg avatar image.
682     * @return Avatar creation response.
683     */
684    public AvatarUploadResponse uploadAvatar(File file) {
685        try {
686            return uploadAvatar(new FileInputStream(file), file.getName());
687        } catch (FileNotFoundException e) {
688            throw new RuntimeException(e);
689        }
690    }
691
692    /**
693     *  Upload avatar image to user account. Supported formats are JPG and PNG.
694     *  The image size cannot exceed 1024 * 1024 pixels or 1MB.
695     *
696     * @param file             {@link File} containg avatar image.
697     * @param progressListener {@link ProgressListener} set if you want to track upload progress
698     * @return Avatar creation response.
699     */
700    public AvatarUploadResponse uploadAvatar(File file, ProgressListener progressListener) {
701        try {
702            return uploadAvatar(new FileInputStream(file), file.getName(), progressListener);
703        } catch (FileNotFoundException e) {
704            throw new RuntimeException(e);
705        }
706    }
707
708    /**
709     * Upload avatar image to user account. Supported formats are JPG and PNG.
710     * The image size cannot exceed 1024 * 1024 pixels or 1MB.
711     *
712     * @param content  {@link InputStream} containing image data
713     * @param fileName file name with extention what will be used to determine content type
714     * @return Avatar creation response.
715     */
716    public AvatarUploadResponse uploadAvatar(InputStream content, String fileName) {
717        return uploadAvatar(content, fileName, null);
718    }
719
720    /**
721     *  Upload avatar image to user account. Supported formats are JPG and PNG.
722     *  The image size cannot exceed 1024 * 1024 pixels or 1MB.
723     *
724     * @param content          {@link InputStream} containing image data
725     * @param fileName         file name with extention what will be used to determine content type
726     * @param progressListener {@link ProgressListener} set if you want to track upload progress
727     * @return Avatar creation response.
728     */
729    public AvatarUploadResponse uploadAvatar(InputStream content, String fileName, ProgressListener progressListener) {
730        URL url = USER_AVATAR_TEMPLATE.build(getAPI().getBaseURL(), this.getID());
731        BoxImageMultipartRequest request = new BoxImageMultipartRequest(getAPI(), url, "pic");
732        request.setFile(content, fileName);
733
734        BoxAPIResponse response;
735        if (progressListener != null) {
736            response = request.send(progressListener);
737        } else {
738            response = request.send();
739        }
740
741        return parseUploadAvatarResponse(response);
742    }
743
744    /**
745     * Removes avatar from user account.
746     */
747    public void deleteAvatar() {
748        URL url = USER_AVATAR_TEMPLATE.build(getAPI().getBaseURL(), this.getID());
749        BoxAPIRequest request = new BoxAPIRequest(getAPI(), url, DELETE);
750        request.send();
751    }
752
753    private AvatarUploadResponse parseUploadAvatarResponse(BoxAPIResponse response) {
754        JsonObject responseObject = Json.parse(response.bodyToString()).asObject();
755        JsonObject picUrls = responseObject.get("pic_urls").asObject();
756        return new AvatarUploadResponse(
757            picUrls.getString("small", null),
758            picUrls.getString("large", null),
759            picUrls.getString("preview", null)
760        );
761    }
762
763    /**
764     * Enumerates the possible roles that a user can have within an enterprise.
765     */
766    public enum Role {
767        /**
768         * The user is an administrator of their enterprise.
769         */
770        ADMIN("admin"),
771
772        /**
773         * The user is a co-administrator of their enterprise.
774         */
775        COADMIN("coadmin"),
776
777        /**
778         * The user is a regular user within their enterprise.
779         */
780        USER("user");
781
782        private final String jsonValue;
783
784        Role(String jsonValue) {
785            this.jsonValue = jsonValue;
786        }
787
788        static Role fromJSONValue(String jsonValue) {
789            return Role.valueOf(jsonValue.toUpperCase());
790        }
791
792        String toJSONValue() {
793            return this.jsonValue;
794        }
795    }
796
797    /**
798     * Enumerates the possible statuses that a user's account can have.
799     */
800    public enum Status {
801        /**
802         * The user's account is active.
803         */
804        ACTIVE("active"),
805
806        /**
807         * The user's account is inactive.
808         */
809        INACTIVE("inactive"),
810
811        /**
812         * The user's account cannot delete or edit content.
813         */
814        CANNOT_DELETE_EDIT("cannot_delete_edit"),
815
816        /**
817         * The user's account cannot delete, edit, or upload content.
818         */
819        CANNOT_DELETE_EDIT_UPLOAD("cannot_delete_edit_upload");
820
821        private final String jsonValue;
822
823        Status(String jsonValue) {
824            this.jsonValue = jsonValue;
825        }
826
827        static Status fromJSONValue(String jsonValue) {
828            return Status.valueOf(jsonValue.toUpperCase());
829        }
830
831        String toJSONValue() {
832            return this.jsonValue;
833        }
834    }
835
836    /**
837     * Contains information about a BoxUser.
838     */
839    public class Info extends BoxCollaborator.Info {
840        private String login;
841        private Role role;
842        private String language;
843        private String timezone;
844        private long spaceAmount;
845        private long spaceUsed;
846        private long maxUploadSize;
847        private boolean canSeeManagedUsers;
848        private boolean isSyncEnabled;
849        private boolean isExternalCollabRestricted;
850        private Status status;
851        private String jobTitle;
852        private String phone;
853        private String address;
854        private String avatarURL;
855        private boolean isExemptFromDeviceLimits;
856        private boolean isExemptFromLoginVerification;
857        private boolean isPasswordResetRequired;
858        private boolean isPlatformAccessOnly;
859        private String externalAppUserId;
860        private BoxEnterprise enterprise;
861        private List<String> myTags;
862        private String hostname;
863        private Map<String, String> trackingCodes;
864
865        /**
866         * Constructs an empty Info object.
867         */
868        public Info() {
869            super();
870        }
871
872        /**
873         * Constructs an Info object by parsing information from a JSON string.
874         *
875         * @param json the JSON string to parse.
876         */
877        public Info(String json) {
878            super(json);
879        }
880
881        Info(JsonObject jsonObject) {
882            super(jsonObject);
883        }
884
885        @Override
886        public BoxUser getResource() {
887            return BoxUser.this;
888        }
889
890        /**
891         * Gets the email address the user uses to login.
892         *
893         * @return the email address the user uses to login.
894         */
895        public String getLogin() {
896            return this.login;
897        }
898
899        /**
900         * Sets the email address the user uses to login. The new login must be one of the user's already confirmed
901         * email aliases.
902         *
903         * @param login one of the user's confirmed email aliases.
904         */
905        public void setLogin(String login) {
906            this.login = login;
907            this.addPendingChange("login", login);
908        }
909
910        /**
911         * Gets the user's enterprise role.
912         *
913         * @return the user's enterprise role.
914         */
915        public Role getRole() {
916            return this.role;
917        }
918
919        /**
920         * Sets the user's role in their enterprise.
921         *
922         * @param role the user's new role in their enterprise.
923         */
924        public void setRole(Role role) {
925            this.role = role;
926            this.addPendingChange("role", role.name().toLowerCase());
927        }
928
929        /**
930         * Gets the language of the user.
931         *
932         * @return the language of the user.
933         */
934        public String getLanguage() {
935            return this.language;
936        }
937
938        /**
939         * Sets the language of the user.
940         *
941         * @param language the new language of the user.
942         */
943        public void setLanguage(String language) {
944            this.language = language;
945            this.addPendingChange("language", language);
946        }
947
948        /**
949         * Gets the timezone of the user.
950         *
951         * @return the timezone of the user.
952         */
953        public String getTimezone() {
954            return this.timezone;
955        }
956
957        /**
958         * Sets the timezone of the user.
959         *
960         * @param timezone the new timezone of the user.
961         */
962        public void setTimezone(String timezone) {
963            this.timezone = timezone;
964            this.addPendingChange("timezone", timezone);
965        }
966
967        /**
968         * Gets the user's total available space in bytes.
969         *
970         * @return the user's total available space in bytes.
971         */
972        public long getSpaceAmount() {
973            return this.spaceAmount;
974        }
975
976        /**
977         * Sets the user's total available space in bytes.
978         *
979         * @param spaceAmount the new amount of space available to the user in bytes, or -1 for unlimited storage.
980         */
981        public void setSpaceAmount(long spaceAmount) {
982            this.spaceAmount = spaceAmount;
983            this.addPendingChange("space_amount", spaceAmount);
984        }
985
986        /**
987         * Gets the amount of space the user has used in bytes.
988         *
989         * @return the amount of space the user has used in bytes.
990         */
991        public long getSpaceUsed() {
992            return this.spaceUsed;
993        }
994
995        /**
996         * Gets the maximum individual file size in bytes the user can have.
997         *
998         * @return the maximum individual file size in bytes the user can have.
999         */
1000        public long getMaxUploadSize() {
1001            return this.maxUploadSize;
1002        }
1003
1004        /**
1005         * Gets the user's current account status.
1006         *
1007         * @return the user's current account status.
1008         */
1009        public Status getStatus() {
1010            return this.status;
1011        }
1012
1013        /**
1014         * Sets the user's current account status.
1015         *
1016         * @param status the user's new account status.
1017         */
1018        public void setStatus(Status status) {
1019            this.status = status;
1020            this.addPendingChange("status", status.name().toLowerCase());
1021        }
1022
1023        /**
1024         * Gets the job title of the user.
1025         *
1026         * @return the job title of the user.
1027         */
1028        public String getJobTitle() {
1029            return this.jobTitle;
1030        }
1031
1032        /**
1033         * Sets the job title of the user.
1034         *
1035         * @param jobTitle the new job title of the user.
1036         */
1037        public void setJobTitle(String jobTitle) {
1038            this.jobTitle = jobTitle;
1039            this.addPendingChange("job_title", jobTitle);
1040        }
1041
1042        /**
1043         * Gets the phone number of the user.
1044         *
1045         * @return the phone number of the user.
1046         */
1047        public String getPhone() {
1048            return this.phone;
1049        }
1050
1051        /**
1052         * Sets the phone number of the user.
1053         *
1054         * @param phone the new phone number of the user.
1055         */
1056        public void setPhone(String phone) {
1057            this.phone = phone;
1058            this.addPendingChange("phone", phone);
1059        }
1060
1061        /**
1062         * Gets the address of the user.
1063         *
1064         * @return the address of the user.
1065         */
1066        public String getAddress() {
1067            return this.address;
1068        }
1069
1070        /**
1071         * Sets the address of the user.
1072         *
1073         * @param address the new address of the user.
1074         */
1075        public void setAddress(String address) {
1076            this.address = address;
1077            this.addPendingChange("address", address);
1078        }
1079
1080        /**
1081         * Gets the URL of the user's avatar.
1082         *
1083         * @return the URL of the user's avatar.
1084         */
1085        public String getAvatarURL() {
1086            return this.avatarURL;
1087        }
1088
1089        /**
1090         * Gets the enterprise that the user belongs to.
1091         *
1092         * @return the enterprise that the user belongs to.
1093         */
1094        public BoxEnterprise getEnterprise() {
1095            return this.enterprise;
1096        }
1097
1098        /**
1099         * Removes the user from their enterprise and converts them to a standalone free user.
1100         */
1101        public void removeEnterprise() {
1102            this.removeChildObject("enterprise");
1103            this.enterprise = null;
1104            this.addChildObject("enterprise", null);
1105        }
1106
1107        /**
1108         * Gets whether or not the user can use Box Sync.
1109         *
1110         * @return true if the user can use Box Sync; otherwise false.
1111         */
1112        public boolean getIsSyncEnabled() {
1113            return this.isSyncEnabled;
1114        }
1115
1116        /**
1117         * Sets whether or not the user can use Box Sync.
1118         *
1119         * @param enabled whether or not the user can use Box Sync.
1120         */
1121        public void setIsSyncEnabled(boolean enabled) {
1122            this.isSyncEnabled = enabled;
1123            this.addPendingChange("is_sync_enabled", enabled);
1124        }
1125
1126        /**
1127         * Gets whether this user is allowed or not to collaborate with users outside their enterprise.
1128         *
1129         * @return true if this user is not allowed to collaborate with users outside their enterprise; otherwise false.
1130         */
1131        public boolean getIsExternalCollabRestricted() {
1132            return this.isExternalCollabRestricted;
1133        }
1134
1135        /**
1136         * Sets whether this user is allowed or not to collaborate with users outside their enterprise.
1137         *
1138         * @param isExternalCollabRestricted whether the user is allowed to collaborate outside their enterprise.
1139         */
1140        public void setIsExternalCollabRestricted(boolean isExternalCollabRestricted) {
1141            this.isExternalCollabRestricted = isExternalCollabRestricted;
1142            this.addPendingChange("is_external_collab_restricted", isExternalCollabRestricted);
1143        }
1144
1145        /**
1146         * Gets whether or not the user can see other enterprise users in their contact list.
1147         *
1148         * @return true if the user can see other enterprise users in their contact list; otherwise false.
1149         */
1150        public boolean getCanSeeManagedUsers() {
1151            return this.canSeeManagedUsers;
1152        }
1153
1154        /**
1155         * Sets whether or not the user can see other enterprise users in their contact list.
1156         *
1157         * @param canSeeManagedUsers whether or not the user can see other enterprise users in their contact list.
1158         */
1159        public void setCanSeeManagedUsers(boolean canSeeManagedUsers) {
1160            this.canSeeManagedUsers = canSeeManagedUsers;
1161            this.addPendingChange("can_see_managed_users", canSeeManagedUsers);
1162        }
1163
1164        /**
1165         * Gets whether or not the user is exempt from enterprise device limits.
1166         *
1167         * @return true if the user is exempt from enterprise device limits; otherwise false.
1168         */
1169        public boolean getIsExemptFromDeviceLimits() {
1170            return this.isExemptFromDeviceLimits;
1171        }
1172
1173        /**
1174         * Sets whether or not the user is exempt from enterprise device limits.
1175         *
1176         * @param isExemptFromDeviceLimits whether or not the user is exempt from enterprise device limits.
1177         */
1178        public void setIsExemptFromDeviceLimits(boolean isExemptFromDeviceLimits) {
1179            this.isExemptFromDeviceLimits = isExemptFromDeviceLimits;
1180            this.addPendingChange("is_exempt_from_device_limits", isExemptFromDeviceLimits);
1181        }
1182
1183        /**
1184         * Gets whether or not the user must use two-factor authentication.
1185         *
1186         * @return true if the user must use two-factor authentication; otherwise false.
1187         */
1188        public boolean getIsExemptFromLoginVerification() {
1189            return this.isExemptFromLoginVerification;
1190        }
1191
1192        /**
1193         * Sets whether or not the user must use two-factor authentication.
1194         *
1195         * @param isExemptFromLoginVerification whether or not the user must use two-factor authentication.
1196         */
1197        public void setIsExemptFromLoginVerification(boolean isExemptFromLoginVerification) {
1198            this.isExemptFromLoginVerification = isExemptFromLoginVerification;
1199            this.addPendingChange("is_exempt_from_login_verification", isExemptFromLoginVerification);
1200        }
1201
1202        /**
1203         * Gets whether or not the user is required to reset password.
1204         *
1205         * @return true if the user is required to reset password; otherwise false.
1206         */
1207        public boolean getIsPasswordResetRequired() {
1208            return this.isPasswordResetRequired;
1209        }
1210
1211        /**
1212         * Sets whether or not the user is required to reset password.
1213         *
1214         * @param isPasswordResetRequired whether or not the user is required to reset password.
1215         */
1216        public void setIsPasswordResetRequired(boolean isPasswordResetRequired) {
1217            this.isPasswordResetRequired = isPasswordResetRequired;
1218            this.addPendingChange("is_password_reset_required", isPasswordResetRequired);
1219        }
1220
1221        /**
1222         * Gets whether or not the user we are creating is an app user with Box Developer Edition.
1223         *
1224         * @return true if the new user is an app user for Box Developer Addition; otherwise false.
1225         */
1226        public boolean getIsPlatformAccessOnly() {
1227            return this.isPlatformAccessOnly;
1228        }
1229
1230        /**
1231         * Gets the external app user id that has been set for the app user.
1232         *
1233         * @return the external app user id.
1234         */
1235        public String getExternalAppUserId() {
1236            return this.externalAppUserId;
1237        }
1238
1239        /**
1240         * Sets the external app user id.
1241         *
1242         * @param externalAppUserId external app user id.
1243         */
1244        public void setExternalAppUserId(String externalAppUserId) {
1245            this.externalAppUserId = externalAppUserId;
1246            this.addPendingChange("external_app_user_id", externalAppUserId);
1247        }
1248
1249        /**
1250         * Gets the tags for all files and folders owned by this user.
1251         *
1252         * @return the tags for all files and folders owned by this user.
1253         */
1254        public List<String> getMyTags() {
1255            return this.myTags;
1256        }
1257
1258        /**
1259         * Gets the root (protocol, subdomain, domain) of any links that need to be generated for this user.
1260         *
1261         * @return the root (protocol, subdomain, domain) of any links that need to be generated for this user.
1262         */
1263        public String getHostname() {
1264            return this.hostname;
1265        }
1266
1267        /**
1268         * Gets the tracking defined for each entity.
1269         *
1270         * @return a Map with tracking codes.
1271         */
1272        public Map<String, String> getTrackingCodes() {
1273            return this.trackingCodes;
1274        }
1275
1276        /**
1277         * Allows admin to set attributes specific for a group of users.
1278         *
1279         * @param trackingCodes a Map representing the user's new tracking codes
1280         */
1281        public void setTrackingCodes(Map<String, String> trackingCodes) {
1282            this.trackingCodes = trackingCodes;
1283            this.addPendingChange("tracking_codes", toTrackingCodesJson(this.trackingCodes));
1284        }
1285
1286        /**
1287         * Allows the admin to append new tracking codes to the previous existing list.
1288         *
1289         * @param name  the name or `key` of the attribute to set.
1290         * @param value the value of the attribute to set.
1291         */
1292        public void appendTrackingCodes(String name, String value) {
1293            this.getTrackingCodes().put(name, value);
1294            this.addPendingChange("tracking_codes", toTrackingCodesJson(this.trackingCodes));
1295        }
1296
1297        @Override
1298        protected void parseJSONMember(JsonObject.Member member) {
1299            super.parseJSONMember(member);
1300
1301            JsonValue value = member.getValue();
1302            String memberName = member.getName();
1303            try {
1304                if (memberName.equals("login")) {
1305                    this.login = value.asString();
1306                } else if (memberName.equals("role")) {
1307                    this.role = Role.fromJSONValue(value.asString());
1308                } else if (memberName.equals("language")) {
1309                    this.language = value.asString();
1310                } else if (memberName.equals("timezone")) {
1311                    this.timezone = value.asString();
1312                } else if (memberName.equals("space_amount")) {
1313                    this.spaceAmount = Double.valueOf(value.toString()).longValue();
1314                } else if (memberName.equals("space_used")) {
1315                    this.spaceUsed = Double.valueOf(value.toString()).longValue();
1316                } else if (memberName.equals("max_upload_size")) {
1317                    this.maxUploadSize = Double.valueOf(value.toString()).longValue();
1318                } else if (memberName.equals("status")) {
1319                    this.status = Status.fromJSONValue(value.asString());
1320                } else if (memberName.equals("job_title")) {
1321                    this.jobTitle = value.asString();
1322                } else if (memberName.equals("phone")) {
1323                    this.phone = value.asString();
1324                } else if (memberName.equals("address")) {
1325                    this.address = value.asString();
1326                } else if (memberName.equals("avatar_url")) {
1327                    this.avatarURL = value.asString();
1328                } else if (memberName.equals("can_see_managed_users")) {
1329                    this.canSeeManagedUsers = value.asBoolean();
1330                } else if (memberName.equals("is_sync_enabled")) {
1331                    this.isSyncEnabled = value.asBoolean();
1332                } else if (memberName.equals("is_external_collab_restricted")) {
1333                    this.isExternalCollabRestricted = value.asBoolean();
1334                } else if (memberName.equals("is_exempt_from_device_limits")) {
1335                    this.isExemptFromDeviceLimits = value.asBoolean();
1336                } else if (memberName.equals("is_exempt_from_login_verification")) {
1337                    this.isExemptFromLoginVerification = value.asBoolean();
1338                } else if (memberName.equals("is_password_reset_required")) {
1339                    this.isPasswordResetRequired = value.asBoolean();
1340                } else if (memberName.equals("is_platform_access_only")) {
1341                    this.isPlatformAccessOnly = value.asBoolean();
1342                } else if (memberName.equals("external_app_user_id")) {
1343                    this.externalAppUserId = value.asString();
1344                } else if (memberName.equals("enterprise")) {
1345                    JsonObject jsonObject = value.asObject();
1346                    if (this.enterprise == null) {
1347                        this.enterprise = new BoxEnterprise(jsonObject);
1348                    } else {
1349                        this.enterprise.update(jsonObject);
1350                    }
1351                } else if (memberName.equals("my_tags")) {
1352                    this.myTags = this.parseMyTags(value.asArray());
1353                } else if (memberName.equals("hostname")) {
1354                    this.hostname = value.asString();
1355                } else if (memberName.equals("tracking_codes")) {
1356                    this.trackingCodes = this.parseTrackingCodes(value.asArray());
1357                }
1358            } catch (Exception e) {
1359                throw new BoxDeserializationException(memberName, value.toString(), e);
1360            }
1361
1362        }
1363
1364        private List<String> parseMyTags(JsonArray jsonArray) {
1365            List<String> myTags = new ArrayList<>(jsonArray.size());
1366            for (JsonValue value : jsonArray) {
1367                myTags.add(value.asString());
1368            }
1369
1370            return myTags;
1371        }
1372
1373        private Map<String, String> parseTrackingCodes(JsonArray jsonArray) {
1374            Map<String, String> result = new HashMap<>();
1375            if (jsonArray == null) {
1376                return null;
1377            }
1378            List<JsonValue> valuesList = jsonArray.values();
1379            for (JsonValue jsonValue : valuesList) {
1380                JsonObject object = jsonValue.asObject();
1381                result.put(object.get("name").asString(), object.get("value").asString());
1382            }
1383            return result;
1384        }
1385    }
1386}