001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.svl.functions; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.*; 022import java.util.concurrent.*; 023 024import org.apache.juneau.commons.svl.*; 025 026/** 027 * Random / UUID functions for the {@code #{...}} script catalog. 028 * 029 * <p> 030 * All {@code rand*} functions evaluate per-resolve and are NOT folded by stable-value folding 031 * (stable folding is {@link Var}-side only — function evaluation always runs). Use a 032 * {@code Supplier<String>} field to get fresh-per-{@code .get()} semantics, or a bare 033 * {@code String} field to capture once at bean construction. 034 * 035 * <h5 class='section'>Cryptographic strength:</h5> 036 * <ul> 037 * <li>{@link Rand}, {@link RandInt}, {@link RandLong}, {@link RandString}, {@link RandChoice} 038 * are backed by {@link ThreadLocalRandom}. Threadsafe; <b>NOT</b> cryptographically secure. 039 * Do not use for tokens, secrets, or session ids. 040 * <li>{@link Uuid} is backed by {@link UUID#randomUUID()} which uses {@link java.security.SecureRandom} 041 * — cryptographically strong. 042 * </ul> 043 */ 044public final class RandomFunctions { 045 046 private RandomFunctions() {} 047 048 /** All function classes in this category. */ 049 @SuppressWarnings({ 050 "java:S2386", // ALL is an immutable compile-time registry; exposed as an array for the cross-package/varargs functions(...) API, so visibility cannot be reduced. 051 "unchecked" // Cast is safe: type verified by caller context. 052 }) 053 public static final Class<? extends VarFunction>[] ALL = new Class[] { 054 Rand.class, RandInt.class, RandLong.class, RandString.class, RandChoice.class, Uuid.class 055 }; 056 057 /** Default character set for {@link RandString} when the user supplies no explicit set. */ 058 private static final String ALPHA_NUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 059 060 /** {@code #{rand()}} — random double in {@code [0.0, 1.0)}. */ 061 public static class Rand extends TypedFunction { 062 @Override public String name() { return "rand"; } 063 public String invoke() { return String.valueOf(ThreadLocalRandom.current().nextDouble()); } 064 } 065 066 /** 067 * {@code #{randInt(max)}} — random int in {@code [0, max)}. 068 * 069 * <p> 070 * {@code #{randInt(min, max)}} — random int in {@code [min, max]} (inclusive both ends). 071 */ 072 public static class RandInt extends TypedFunction { 073 @Override public String name() { return "randInt"; } 074 public String invoke(int max) { 075 if (max <= 0) throw iaex("randInt: max must be > 0"); 076 return String.valueOf(ThreadLocalRandom.current().nextInt(max)); 077 } 078 public String invoke(int min, int max) { 079 if (min > max) throw iaex("randInt: min (%s) must be <= max (%s)", min, max); 080 return String.valueOf(ThreadLocalRandom.current().nextLong(min, (long) max + 1)); 081 } 082 } 083 084 /** {@code #{randLong(min, max)}} — random long in {@code [min, max]} (inclusive both ends). */ 085 public static class RandLong extends TypedFunction { 086 @Override public String name() { return "randLong"; } 087 public String invoke(long min, long max) { 088 if (min > max) throw iaex("randLong: min (%s) must be <= max (%s)", min, max); 089 if (max == Long.MAX_VALUE) 090 return String.valueOf(ThreadLocalRandom.current().nextLong(min, max)); 091 return String.valueOf(ThreadLocalRandom.current().nextLong(min, max + 1)); 092 } 093 } 094 095 /** 096 * {@code #{randString(length)}} — random alphanumeric {@code [A-Za-z0-9]} string of 097 * {@code length} characters. 098 * 099 * <p> 100 * {@code #{randString(length, charset)}} — same, but characters drawn uniformly from the 101 * supplied {@code charset} string. 102 */ 103 public static class RandString extends TypedFunction { 104 @Override public String name() { return "randString"; } 105 public String invoke(int length) { return invoke(length, ALPHA_NUM); } 106 public String invoke(int length, String charset) { 107 if (length < 0) throw iaex("randString: length must be >= 0"); 108 if (ie(charset)) charset = ALPHA_NUM; 109 var rnd = ThreadLocalRandom.current(); 110 var sb = new StringBuilder(length); 111 for (var i = 0; i < length; i++) 112 sb.append(charset.charAt(rnd.nextInt(charset.length()))); 113 return sb.toString(); 114 } 115 } 116 117 /** {@code #{randChoice(option1, option2, ...)}} — variadic. Returns one arg chosen uniformly. */ 118 public static class RandChoice extends TypedFunction { 119 @Override public String name() { return "randChoice"; } 120 public String invoke(String[] options) { 121 if (options.length == 0) throw iaex("randChoice: at least one option required"); 122 return options[ThreadLocalRandom.current().nextInt(options.length)]; 123 } 124 } 125 126 /** {@code #{uuid()}} — random UUID via {@link UUID#randomUUID()}. Cryptographically strong. */ 127 public static class Uuid extends TypedFunction { 128 @Override public String name() { return "uuid"; } 129 public String invoke() { return UUID.randomUUID().toString(); } 130 } 131}