001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyrighted [year] [name of copyright owner]".
013 *
014 * Copyright 2012-2015 ForgeRock AS. All rights reserved.
015 */
016
017package org.forgerock.json.resource;
018
019import org.forgerock.json.JsonException;
020import org.forgerock.json.JsonPointer;
021
022/**
023 * A sort key which can be used to specify the order in which JSON resources
024 * should be included in the results of a query request.
025 */
026public final class SortKey {
027
028    /**
029     * Creates a new ascending-order sort key for the provided JSON field.
030     *
031     * @param field
032     *            The sort key field.
033     * @return A new ascending-order sort key.
034     */
035    public static SortKey ascendingOrder(final JsonPointer field) {
036        return new SortKey(field, true);
037    }
038
039    /**
040     * Creates a new ascending-order sort key for the provided JSON field.
041     *
042     * @param field
043     *            The sort key field.
044     * @return A new ascending-order sort key.
045     * @throws IllegalArgumentException
046     *             If {@code field} is not a valid JSON pointer.
047     */
048    public static SortKey ascendingOrder(final String field) {
049        try {
050            return ascendingOrder(new JsonPointer(field));
051        } catch (JsonException e) {
052            throw new IllegalArgumentException(e.getMessage());
053        }
054    }
055
056    /**
057     * Creates a new descending-order sort key for the provided JSON field.
058     *
059     * @param field
060     *            The sort key field.
061     * @return A new descending-order sort key.
062     */
063    public static SortKey descendingOrder(final JsonPointer field) {
064        return new SortKey(field, false);
065    }
066
067    /**
068     * Creates a new descending-order sort key for the provided JSON field.
069     *
070     * @param field
071     *            The sort key field.
072     * @return A new descending-order sort key.
073     * @throws IllegalArgumentException
074     *             If {@code field} is not a valid JSON pointer.
075     */
076    public static SortKey descendingOrder(final String field) {
077        try {
078            return descendingOrder(new JsonPointer(field));
079        } catch (JsonException e) {
080            throw new IllegalArgumentException(e.getMessage());
081        }
082    }
083
084    /**
085     * Creates a new sort key having the same field as the provided key, but in
086     * reverse sort order.
087     *
088     * @param key
089     *            The sort key to be reversed.
090     * @return The reversed sort key.
091     */
092    public static SortKey reverseOrder(final SortKey key) {
093        return new SortKey(key.field, !key.isAscendingOrder);
094    }
095
096    private final JsonPointer field;
097
098    private final boolean isAscendingOrder;
099
100    private SortKey(final JsonPointer field, final boolean isAscendingOrder) {
101        this.field = field;
102        this.isAscendingOrder = isAscendingOrder;
103    }
104
105    /**
106     * Returns the sort key field.
107     *
108     * @return The sort key field.
109     */
110    public JsonPointer getField() {
111        return field;
112    }
113
114    /**
115     * Returns {@code true} if this sort key is in ascending order, or
116     * {@code false} if it is in descending order.
117     *
118     * @return {@code true} if this sort key is in ascending order, or
119     *         {@code false} if it is in descending ord)er.
120     */
121    public boolean isAscendingOrder() {
122        return isAscendingOrder;
123    }
124
125    /**
126     * Parses the provided string as a sort key. If the string does not begin
127     * with a plus or minus symbol, then the sort key will default to ascending
128     * order.
129     *
130     * @param s
131     *            The string representation of a sort key as specified in
132     *            {@link #toString()}.
133     * @return The parsed sort key.
134     * @throws IllegalArgumentException
135     *             If {@code s} is not a valid sort key.
136     */
137    public static SortKey valueOf(String s) {
138        if (s.length() == 0) {
139            throw new IllegalArgumentException("Empty sort key");
140        }
141
142        switch (s.charAt(0)) {
143        case '-':
144            return descendingOrder(s.substring(1));
145        case '+':
146            return ascendingOrder(s.substring(1));
147        default:
148            return ascendingOrder(s);
149        }
150    }
151
152    /**
153     * Returns the string representation of this sort key. It will be composed
154     * of a plus symbol, if the key is ascending, or a minus symbol, if the key
155     * is descending, followed by the field name.
156     *
157     * @return The string representation of this sort key.
158     */
159    public String toString() {
160        final StringBuilder builder = new StringBuilder();
161        builder.append(isAscendingOrder ? '+' : '-');
162        builder.append(field);
163        return builder.toString();
164    }
165}