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.lang; 018 019import static org.apache.juneau.commons.utils.ThrowableUtils.*; 020 021import java.util.*; 022 023/** 024 * Stores a set of ASCII characters for quick lookup. 025 */ 026public class AsciiSet { 027 /** 028 * Builder class. 029 */ 030 public static class Builder { 031 final boolean[] store = new boolean[128]; 032 033 /** 034 * Create a new {@link AsciiSet} object with the contents of this builder. 035 * 036 * @return A new {link AsciiSet} object. 037 */ 038 public AsciiSet build() { 039 return new AsciiSet(store); 040 } 041 042 /** 043 * Adds a set of characters to this set. 044 * 045 * @param value The characters to keep in this store. 046 * @return This object. 047 */ 048 public Builder chars(char...value) { 049 for (var i = 0; i < value.length; i++) 050 if (value[i] < 128) 051 store[value[i]] = true; 052 return this; 053 } 054 055 /** 056 * Adds a set of characters to this set. 057 * 058 * @param value The characters to keep in this store. 059 * @return This object. 060 */ 061 public AsciiSet.Builder chars(String value) { 062 for (var i = 0; i < value.length(); i++) { 063 var c = value.charAt(i); 064 if (c < 128) 065 store[c] = true; 066 } 067 return this; 068 } 069 070 /** 071 * Adds a range of characters to this set. 072 * 073 * @param start The start character. 074 * @param end The end character. 075 * @return This object. 076 */ 077 public AsciiSet.Builder range(char start, char end) { 078 for (var c = start; c <= end; c++) 079 if (c < 128) 080 store[c] = true; 081 return this; 082 } 083 084 /** 085 * Shortcut for calling multiple ranges. 086 * 087 * @param value Strings of the form "A-Z" where A and Z represent the first and last characters in the range. 088 * @return This object. 089 */ 090 public AsciiSet.Builder ranges(String...value) { 091 for (var ss : value) { 092 if (ss.length() != 3 || ss.charAt(1) != '-') 093 throw illegalArg("Value passed to ranges() must be 3 characters"); 094 range(ss.charAt(0), ss.charAt(2)); 095 } 096 return this; 097 } 098 } 099 100 /** 101 * Creates a builder for an ASCII set. 102 * 103 * @return A new builder. 104 */ 105 public static AsciiSet.Builder create() { 106 return new Builder(); 107 } 108 109 /** 110 * Creates an ASCII set with the specified characters. 111 * 112 * @param value The characters to keep in this store. 113 * @return A new object. 114 */ 115 public static AsciiSet of(String value) { 116 return new Builder().chars(value).build(); 117 } 118 119 private final boolean[] store; 120 121 AsciiSet(boolean[] store) { 122 this.store = Arrays.copyOf(store, store.length); 123 } 124 125 /** 126 * Returns <jk>true</jk> if the specified character is in this store. 127 * 128 * @param value The character to check. 129 * @return <jk>true</jk> if the specified character is in this store. 130 */ 131 public boolean contains(char value) { 132 if (value > 127) 133 return false; 134 return store[value]; 135 } 136 137 /** 138 * Returns <jk>true</jk> if the specified string contains at least one character in this set. 139 * 140 * @param value The string to test. 141 * @return <jk>true</jk> if the string is not null and contains at least one character in this set. 142 */ 143 public boolean contains(CharSequence value) { 144 if (value == null) 145 return false; 146 for (var i = 0; i < value.length(); i++) 147 if (contains(value.charAt(i))) 148 return true; 149 return false; 150 } 151 152 /** 153 * Returns <jk>true</jk> if the specified character is in this store. 154 * 155 * @param value The character to check. 156 * @return <jk>true</jk> if the specified character is in this store. 157 */ 158 public boolean contains(int value) { 159 if (value < 0 || value > 127) 160 return false; 161 return store[value]; 162 } 163 164 /** 165 * Returns <jk>true</jk> if the specified string contains only characters in this set. 166 * 167 * @param value The string to test. 168 * @return 169 * <jk>true</jk> if the string contains only characters in this set. 170 * <br>Nulls always return <jk>false</jk>. 171 * <br>Blanks always return <jk>true</jk>. 172 */ 173 public boolean containsOnly(String value) { 174 if (value == null) 175 return false; 176 for (var i = 0; i < value.length(); i++) 177 if (! contains(value.charAt(i))) 178 return false; 179 return true; 180 } 181 182 /** 183 * Copies an existing {@link AsciiSet} so that you can augment it with additional values. 184 * 185 * @return A builder initialized to the same characters in the copied set. 186 */ 187 public AsciiSet.Builder copy() { 188 var b = new Builder(); 189 System.arraycopy(store, 0, b.store, 0, 128); 190 return b; 191 } 192}