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