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 © 2010–2011 ApexIdentity Inc. All rights reserved. 015 * Portions Copyrighted 2011-2016 ForgeRock AS. 016 */ 017 018package org.forgerock.json; 019 020import java.io.File; 021import java.net.MalformedURLException; 022import java.net.URI; 023import java.net.URISyntaxException; 024import java.net.URL; 025import java.nio.charset.Charset; 026import java.nio.charset.IllegalCharsetNameException; 027import java.nio.charset.UnsupportedCharsetException; 028import java.util.ArrayList; 029import java.util.LinkedHashSet; 030import java.util.List; 031import java.util.Set; 032import java.util.UUID; 033import java.util.regex.Pattern; 034import java.util.regex.PatternSyntaxException; 035 036import org.forgerock.util.Function; 037import org.forgerock.util.Utils; 038import org.forgerock.util.time.Duration; 039 040/** 041 * This class contains the utility functions to convert a {@link JsonValue} to another type. 042 */ 043public final class JsonValueFunctions { 044 045 private JsonValueFunctions() { 046 } 047 048 //@Checkstyle:off 049 private static final Function<JsonValue, Charset, JsonValueException> TO_CHARSET = 050 new Function<JsonValue, Charset, JsonValueException>() { 051 @Override 052 public Charset apply(JsonValue value) throws JsonValueException { 053 try { 054 return value.isNull() ? null : Charset.forName(value.asString()); 055 } catch (final IllegalCharsetNameException | UnsupportedCharsetException e) { 056 throw new JsonValueException(value, e); 057 } 058 } 059 }; 060 061 private static final Function<JsonValue, Duration, JsonValueException> TO_DURATION = 062 new Function<JsonValue, Duration, JsonValueException>() { 063 @Override 064 public Duration apply(JsonValue value) throws JsonValueException { 065 try { 066 return value.isNull() ? null : Duration.duration(value.asString()); 067 } catch (final IllegalArgumentException iae) { 068 throw new JsonValueException(value, iae); 069 } 070 } 071 }; 072 073 private static final Function<JsonValue, File, JsonValueException> TO_FILE = 074 new Function<JsonValue, File, JsonValueException>() { 075 @Override 076 public File apply(JsonValue value) throws JsonValueException { 077 return value.isNull() ? null : new File(value.asString()); 078 } 079 }; 080 081 private static final Function<JsonValue, Pattern, JsonValueException> TO_PATTERN = 082 new Function<JsonValue, Pattern, JsonValueException>() { 083 @Override 084 public Pattern apply(JsonValue value) throws JsonValueException { 085 try { 086 return value.isNull() ? null : Pattern.compile(value.asString()); 087 } catch (final PatternSyntaxException pse) { 088 throw new JsonValueException(value, pse); 089 } 090 } 091 }; 092 093 private static final Function<JsonValue, JsonPointer, JsonValueException> TO_POINTER = 094 new Function<JsonValue, JsonPointer, JsonValueException>() { 095 @Override 096 public JsonPointer apply(JsonValue value) throws JsonValueException { 097 try { 098 return value.isNull() ? null : new JsonPointer(value.asString()); 099 } catch (final JsonValueException jve) { 100 throw jve; 101 } catch (final JsonException je) { 102 throw new JsonValueException(value, je); 103 } 104 } 105 }; 106 107 private static final Function<JsonValue, URL, JsonValueException> TO_URL = 108 new Function<JsonValue, URL, JsonValueException>() { 109 @Override 110 public URL apply(JsonValue value) throws JsonValueException { 111 try { 112 return value.isNull() ? null : new URL(value.asString()); 113 } catch (final MalformedURLException e) { 114 throw new JsonValueException(value, e); 115 } 116 } 117 }; 118 119 private static final Function<JsonValue, URI, JsonValueException> TO_URI = 120 new Function<JsonValue, URI, JsonValueException>() { 121 @Override 122 public URI apply(JsonValue value) throws JsonValueException { 123 try { 124 return value.isNull() ? null : new URI(value.asString()); 125 } catch (final URISyntaxException use) { 126 throw new JsonValueException(value, use); 127 } 128 } 129 }; 130 131 private static final Function<JsonValue, UUID, JsonValueException> TO_UUID = 132 new Function<JsonValue, UUID, JsonValueException>() { 133 @Override 134 public UUID apply(JsonValue value) throws JsonValueException { 135 try { 136 return value.isNull() ? null : UUID.fromString(value.asString()); 137 } catch (final IllegalArgumentException iae) { 138 throw new JsonValueException(value, iae); 139 } 140 } 141 }; 142 //@Checkstyle:on 143 144 /** 145 * Returns the JSON string value as a character set used for byte 146 * encoding/decoding. If the JSON value is {@code null}, this function returns 147 * {@code null}. 148 * 149 * @return the character set represented by the string value. 150 * @throws JsonValueException 151 * if the JSON value is not a string or the character set 152 * specified is invalid. 153 */ 154 public static Function<JsonValue, Charset, JsonValueException> charset() { 155 return TO_CHARSET; 156 } 157 158 /** 159 * Returns the JSON string value as a {@link Duration}. If the JSON value is {@code null}, this method returns 160 * {@code null}. 161 * 162 * @return the duration represented by the string value. 163 * @throws JsonValueException 164 * if the JSON value is not a string or the duration 165 * specified is invalid. 166 */ 167 public static Function<JsonValue, Duration, JsonValueException> duration() { 168 return TO_DURATION; 169 } 170 171 /** 172 * Returns the JSON string value as an enum constant of the specified enum 173 * type. The string value and enum constants are compared, ignoring case 174 * considerations. If the JSON value is {@code null}, this method returns 175 * {@code null}. 176 * 177 * @param <T> 178 * the enum type sub-class. 179 * @param type 180 * the enum type to match constants with the value. 181 * @return the enum constant represented by the string value. 182 * @throws IllegalArgumentException 183 * if {@code type} does not represent an enum type. or 184 * if the JSON value does not match any of the enum's constants. 185 * @throws NullPointerException 186 * if {@code type} is {@code null}. 187 */ 188 public static <T extends Enum<T>> Function<JsonValue, T, JsonValueException> enumConstant(final Class<T> type) { 189 return new Function<JsonValue, T, JsonValueException>() { 190 @Override 191 public T apply(JsonValue value) throws JsonValueException { 192 return Utils.asEnum(value.asString(), type); 193 } 194 }; 195 } 196 197 /** 198 * Returns the JSON string value as a {@code File} object. If the JSON value 199 * is {@code null}, this method returns {@code null}. 200 * 201 * @return a file represented by the string value. 202 * @throws JsonValueException 203 * if the JSON value is not a string. 204 */ 205 public static Function<JsonValue, File, JsonValueException> file() { 206 return TO_FILE; 207 } 208 209 /** 210 * Returns the JSON string value as a regular expression pattern. If the 211 * JSON value is {@code null}, this method returns {@code null}. 212 * 213 * @return the compiled regular expression pattern. 214 * @throws JsonValueException 215 * if the pattern is not a string or the value is not a valid 216 * regular expression pattern. 217 */ 218 public static Function<JsonValue, Pattern, JsonValueException> pattern() { 219 return TO_PATTERN; 220 } 221 222 /** 223 * Returns the JSON string value as a JSON pointer. If the JSON value is 224 * {@code null}, this method returns {@code null}. 225 * 226 * @return the JSON pointer represented by the JSON value string. 227 * @throws JsonValueException 228 * if the JSON value is not a string or valid JSON pointer. 229 */ 230 public static Function<JsonValue, JsonPointer, JsonValueException> pointer() { 231 return TO_POINTER; 232 } 233 234 /** 235 * Returns the JSON string value as a uniform resource identifier. If the 236 * JSON value is {@code null}, this method returns {@code null}. 237 * 238 * @return the URI represented by the string value. 239 * @throws JsonValueException 240 * if the given string violates URI syntax. 241 */ 242 public static Function<JsonValue, URI, JsonValueException> uri() { 243 return TO_URI; 244 } 245 246 /** 247 * Returns the JSON string value as a uniform resource locator. If the 248 * JSON value is {@code null}, this method returns {@code null}. 249 * 250 * @return the URL represented by the string value. 251 * @throws JsonValueException 252 * if the given string violates URL syntax. 253 */ 254 public static Function<JsonValue, URL, JsonValueException> url() { 255 return TO_URL; 256 } 257 258 /** 259 * Returns the JSON string value as a universally unique identifier (UUID). 260 * If the JSON value is {@code null}, this method returns {@code null}. 261 * 262 * @return the UUID represented by the JSON value string. 263 * @throws JsonValueException 264 * if the JSON value is not a string or valid UUID. 265 */ 266 public static Function<JsonValue, UUID, JsonValueException> uuid() { 267 return TO_UUID; 268 } 269 270 /** 271 * Returns the JSON value as a {@link List} containing objects whose type 272 * (and value) is specified by a transformation function. If the value is 273 * {@code null}, this method returns {@code null}. It is up to to the 274 * transformation function to transform/enforce source types of the elements 275 * in the Json source collection. If any of the elements of the list are not of 276 * the appropriate type, or the type-transformation cannot occur, 277 * the exception specified by the transformation function is thrown. 278 * 279 * @param <V> 280 * the type of elements in this list 281 * @param <E> 282 * the type of exception thrown by the transformation function 283 * @param transformFunction 284 * a {@link Function} to transform an element of the JsonValue list 285 * to the desired type 286 * @return the list value, or {@code null} if no value. 287 * @throws E 288 * if the JSON value is not a {@code List}, not a {@code Set}, contains an 289 * unexpected type, or contains an element that cannot be transformed 290 * @throws NullPointerException 291 * if {@code transformFunction} is {@code null}. 292 */ 293 public static <V, E extends Exception> Function<JsonValue, List<V>, E> listOf( 294 final Function<JsonValue, V, E> transformFunction) throws E { 295 return new Function<JsonValue, List<V>, E>() { 296 @Override 297 public List<V> apply(JsonValue value) throws E { 298 if (value.isCollection()) { 299 final List<V> list = new ArrayList<>(value.size()); 300 for (JsonValue elem : value) { 301 list.add(elem.as(transformFunction)); 302 } 303 return list; 304 } 305 return null; 306 } 307 }; 308 } 309 310 /** 311 * Returns the JSON value as a {@link Set} containing objects whose type 312 * (and value) is specified by a transformation function. If the value is 313 * {@code null}, this method returns {@code null}. It is up to to the 314 * transformation function to transform/enforce source types of the elements 315 * in the Json source collection. If called on an object which wraps a List, 316 * this method will drop duplicates performing element comparisons using 317 * equals/hashCode. If any of the elements of the collection are not of 318 * the appropriate type, or the type-transformation cannot occur, the 319 * exception specified by the transformation function is thrown. 320 * 321 * @param <V> 322 * the type of elements in this set 323 * @param <E> 324 * the type of exception thrown by the transformation function 325 * @param transformFunction 326 * a {@link Function} to transform an element of the JsonValue set 327 * to the desired type 328 * @return the set value, or {@code null} if no value. 329 * @throws E 330 * if the JSON value is not a {@code Set}, contains an 331 * unexpected type, or contains an element that cannot be 332 * transformed 333 * @throws NullPointerException 334 * if {@code transformFunction} is {@code null}. 335 */ 336 public static <V, E extends Exception> Function<JsonValue, Set<V>, E> setOf( 337 final Function<JsonValue, V, E> transformFunction) throws E { 338 return new Function<JsonValue, Set<V>, E>() { 339 @Override 340 public Set<V> apply(JsonValue value) throws E { 341 if (value.isCollection()) { 342 final Set<V> set = new LinkedHashSet<>(value.size()); 343 for (JsonValue elem : value) { 344 set.add(elem.as(transformFunction)); 345 } 346 return set; 347 } 348 return null; 349 } 350 }; 351 } 352}