001package com.box.sdk;
002
003import com.eclipsesource.json.JsonArray;
004import com.eclipsesource.json.JsonObject;
005import com.eclipsesource.json.JsonValue;
006
007/**
008 * The Metadata class represents one type instance of Box metadata.
009 *
010 * Learn more about Box metadata:
011 * https://developers.box.com/metadata-api/
012 */
013public class Metadata {
014    private final JsonObject values;
015    private JsonArray operations;
016
017    /**
018     * Creates an empty metadata.
019     */
020    public Metadata() {
021        this.values = new JsonObject();
022    }
023
024    /**
025     * Creates a new metadata.
026     * @param values the initial metadata values.
027     */
028    Metadata(JsonObject values) {
029        this.values = values;
030    }
031
032    /**
033     * Creates a copy of another metadata.
034     * @param other the other metadata object to copy.
035     */
036    public Metadata(Metadata other) {
037        this.values = new JsonObject(other.values);
038    }
039
040    /**
041     * Returns the 36 character UUID to identify the metadata object.
042     * @return the metadata ID.
043     */
044    public String getID() {
045        return this.get("/$id");
046    }
047
048    /**
049     * Returns the metadata type.
050     * @return the metadata type.
051     */
052    public String getTypeName() {
053        return this.get("/$type");
054    }
055
056    /**
057     * Returns the parent object ID (typically the file ID).
058     * @return the parent object ID.
059     */
060    public String getParentID() {
061        return this.get("/$parent");
062    }
063
064    /**
065     * Adds a new metdata value.
066     * @param path the path that designates the key. Must be prefixed with a "/".
067     * @param value the value.
068     * @return this metadata object.
069     */
070    public Metadata add(String path, String value) {
071        this.values.add(this.pathToProperty(path), value);
072        this.addOp("add", path, value);
073        return this;
074    }
075
076    /**
077     * Replaces an existing metdata value.
078     * @param path the path that designates the key. Must be prefixed with a "/".
079     * @param value the value.
080     * @return this metadata object.
081     */
082    public Metadata replace(String path, String value) {
083        this.values.set(this.pathToProperty(path), value);
084        this.addOp("replace", path, value);
085        return this;
086    }
087
088    /**
089     * Removes an existing metadata value.
090     * @param path the path that designates the key. Must be prefixed with a "/".
091     * @return this metadata object.
092     */
093    public Metadata remove(String path) {
094        this.values.remove(this.pathToProperty(path));
095        this.addOp("remove", path, null);
096        return this;
097    }
098
099    /**
100     * Tests that a property has the expected value.
101     * @param path the path that designates the key. Must be prefixed with a "/".
102     * @param value the expected value.
103     * @return this metadata object.
104     */
105    public Metadata test(String path, String value) {
106        this.addOp("test", path, value);
107        return this;
108    }
109
110    /**
111     * Returns a value.
112     * @param path the path that designates the key. Must be prefixed with a "/".
113     * @return the metadata property value.
114     */
115    public String get(String path) {
116        final JsonValue value = this.values.get(this.pathToProperty(path));
117        if (value == null) {
118            return null;
119        }
120        return value.asString();
121    }
122
123    /**
124     * Returns the JSON patch string with all operations.
125     * @return the JSON patch string.
126     */
127    public String getPatch() {
128        if (this.operations == null) {
129            return "[]";
130        }
131        return this.operations.toString();
132    }
133
134    /**
135     * Returns the JSON representation of this metadata.
136     * @return the JSON representation of this metadata.
137     */
138    @Override
139    public String toString() {
140        return this.values.toString();
141    }
142
143    /**
144     * Converts a JSON patch path to a JSON property name.
145     * Currently the metadata API only supports flat maps.
146     * @param path the path that designates the key.  Must be prefixed with a "/".
147     * @return the JSON property name.
148     */
149    private String pathToProperty(String path) {
150        if (path == null || !path.startsWith("/")) {
151            throw new IllegalArgumentException("Path must be prefixed with a \"/\".");
152        }
153        return path.substring(1);
154    }
155
156    /**
157     * Adds a patch operation.
158     * @param op the operation type. Must be add, replace, remove, or test.
159     * @param path the path that designates the key. Must be prefixed with a "/".
160     * @param value the value to be set.
161     */
162    private void addOp(String op, String path, String value) {
163        if (this.operations == null) {
164            this.operations = new JsonArray();
165        }
166
167        this.operations.add(new JsonObject()
168                .add("op", op)
169                .add("path", path)
170                .add("value", value));
171    }
172}