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