All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.authlete.common.util.MutableJsonPointer Maven / Gradle / Ivy

Go to download

Authlete Java library used commonly by service implementations and the Authlete server.

The newest version!
/*
 * Copyright (C) 2022 Authlete, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.authlete.common.util;


import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;


/**
 * Mutable JSON Pointer.
 *
 * 
 * // From a string.
 * MutableJsonPointer pointer = new MutableJsonPointer("/a/b");
 *
 * // Append one by one.
 * MutableJsonPointer pointer = new MutableJsonPointer().append("a").append("b");
 *
 * // Modify.
 * MutableJsonPointer pointer = new MutableJsonPointer("/a/c").remove().append("b");
 *
 * // Get reference tokens.
 * List<String> tokens = new MutableJsonPointer("/a/b").getReferenceTokens();
 *
 * // Create a JsonPointer instance from a MutableJsonPointer instance.
 * JsonPointer pointer = Json.createPointer(new MutableJsonPointer("/a/b").toString());
 * 
* * @since 3.17 * * @see RFC 6901: JavaScript Object Notation (JSON) Pointer * * @see Jakarta JSON Processing */ public class MutableJsonPointer { private final LinkedList mReferenceTokens = new LinkedList<>(); /** * The default constructor. */ public MutableJsonPointer() { this((String)null); } /** * A copy constructor. * * @param pointer * A {@code MutableJsonPointer} instance from which reference * tokens are copied. */ public MutableJsonPointer(MutableJsonPointer pointer) { if (pointer != null) { mReferenceTokens.addAll(pointer.mReferenceTokens); } } /** * A constructor that receives a JSON pointer. * * @param pointer * A JSON pointer. Both {@code null} and an empty string are * accepted. They represent the whole JSON document. In other * cases, {@code pointer} must start with {@code "/"}. * * @throws IllegalArgumentException * {@code pointer} does not start with {@code "/"} although * it is neither {@code null} nor an empty string. */ public MutableJsonPointer(String pointer) { if (pointer == null) { return; } // If the given string is an empty string. if (pointer.isEmpty()) { // An empty string represents the whole JSON document. return; } // If the given string does not start with "/". if (!pointer.startsWith("/")) { throw new IllegalArgumentException( "A pointer must start with '/' unless it is an empty string."); } // Extract reference tokens from the pointer and add them to // the internal list. Arrays.stream(pointer.split("/")).skip(1) .forEach(referenceToken -> mReferenceTokens.add(referenceToken)); } /** * Append a reference token. * * @param referenceToken * A reference token to append. * * @param doEscape * {@code true} to make this method escape special characters in * {@code referenceToken} before the reference token is appended. * The {@link #escape(String)} method is used for the escape * processing. * * @return * {@code this} object. */ public MutableJsonPointer append(String referenceToken, boolean doEscape) { if (doEscape) { referenceToken = escape(referenceToken); } mReferenceTokens.add(referenceToken); return this; } /** * Append a reference token. * *

* This method is an alias of {@link #append(String, boolean) * append}(referenceToken, true). Special characters in * {@code referenceToken} are escaped. *

* * @param referenceToken * A reference token to append. * * @return * {@code this} object. */ public MutableJsonPointer append(String referenceToken) { return append(referenceToken, true); } /** * Append a reference token. * * @param arrayIndex * A reference token. * * @return * {@code this} object. * * @throws IllegalArgumentException * {@code arrayIndex} is less than 0. */ public MutableJsonPointer append(int arrayIndex) { if (arrayIndex < 0) { throw new IllegalArgumentException( "The argument to append(int) must not be negative."); } return append(String.valueOf(arrayIndex), false); } /** * Remove the last reference token from the end. * *

* Even if this method is called more times than the number of reference * tokens held by this instance, no exception is thrown. *

* * @return * {@code this} object. */ public MutableJsonPointer remove() { mReferenceTokens.pollLast(); return this; } @Override public String toString() { if (mReferenceTokens.size() == 0) { // An empty string which represents the whole JSON document. return ""; } StringBuilder builder = new StringBuilder(); mReferenceTokens.stream().forEach( referenceToken -> builder.append("/").append(referenceToken)); return builder.toString(); } /** * Get the list of reference tokens. * * @return * The list of reference tokens. */ public List getReferenceTokens() { return Collections.unmodifiableList(mReferenceTokens); } /** * Escape special characters in the given string according to the rules * defined in the Section 3. * Syntax of RFC 6901 JavaScript Object Notation (JSON) Pointer. * *

* To be concrete, all {@code "~"} are converted to {@code "~0"} and * then all {@code "/"} are converted to {@code "~1"}. *

* * @param input * A string that may contain special characters * ({@code "~"} and {@code "/"}). * * @return * A new string after conversion. If the given string is * {@code null}, {@code null} is returned. */ public static String escape(String input) { if (input == null) { return null; } // RFC 6901 JavaScript Object notation (JSON) Pointer // 3. Syntax // // json-pointer = *( "/" reference-token ) // reference-token = *( unescaped / escaped ) // unescaped = %x00-2E / %x30-7D / %x7F-10FFFF // ; %x2F ('/') and %x7E ('~') are excluded from 'unescaped' // escaped = "~" ( "0" / "1" ) // ; representing '~' and '/', respectively // return input.replaceAll("~", "~0").replaceAll("/", "~1"); } /** * Unescape special sequences in the given string according to the rules * defined in the Section 4. * Evaluation of RFC 6901 JavaScript Object Notation (JSON) Pointer. * *

* To be concrete, all {@code "~1"} are converted to {@code "/"} and * then all {@code "~0"} are converted to {@code "~"}. *

* * @param input * A string that may contain special sequences ({@code "~0"} and * {@code "~1"}). * * @return * A new string after conversion. If the given string is * {@code null}, {@code null} is returned. */ public static String unescape(String input) { if (input == null) { return null; } // RFC 6901 JavaScript Object notation (JSON) Pointer // 4. Evaluation // // Evaluation of each reference token begins by decoding any escaped // character sequence. This is performed by first transforming any // occurrence of the sequence '~1' to '/', and then transforming any // occurrence of the sequence '~0' to '~'. By performing the // substitutions in this order, an implementation avoids the error of // turning '~01' first into '~1' and then into '/', which would be // incorrect (the string '~01' correctly becomes '~1' after // transformation). return input.replaceAll("~1", "/").replaceAll("~0", "~"); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy