1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.juneau.commons.lang;
18
19 import static org.apache.juneau.commons.utils.ThrowableUtils.*;
20
21 import java.util.*;
22
23 /**
24 * Stores a set of ASCII characters for quick lookup.
25 */
26 public class AsciiSet {
27 /**
28 * Builder class.
29 */
30 public static class Builder {
31 final boolean[] store = new boolean[128];
32
33 /**
34 * Create a new {@link AsciiSet} object with the contents of this builder.
35 *
36 * @return A new {link AsciiSet} object.
37 */
38 public AsciiSet build() {
39 return new AsciiSet(store);
40 }
41
42 /**
43 * Adds a set of characters to this set.
44 *
45 * @param value The characters to keep in this store.
46 * @return This object.
47 */
48 public Builder chars(char...value) {
49 for (var i = 0; i < value.length; i++)
50 if (value[i] < 128)
51 store[value[i]] = true;
52 return this;
53 }
54
55 /**
56 * Adds a set of characters to this set.
57 *
58 * @param value The characters to keep in this store.
59 * @return This object.
60 */
61 public AsciiSet.Builder chars(String value) {
62 for (var i = 0; i < value.length(); i++) {
63 var c = value.charAt(i);
64 if (c < 128)
65 store[c] = true;
66 }
67 return this;
68 }
69
70 /**
71 * Adds a range of characters to this set.
72 *
73 * @param start The start character.
74 * @param end The end character.
75 * @return This object.
76 */
77 public AsciiSet.Builder range(char start, char end) {
78 for (var c = start; c <= end; c++)
79 if (c < 128)
80 store[c] = true;
81 return this;
82 }
83
84 /**
85 * Shortcut for calling multiple ranges.
86 *
87 * @param value Strings of the form "A-Z" where A and Z represent the first and last characters in the range.
88 * @return This object.
89 */
90 public AsciiSet.Builder ranges(String...value) {
91 for (var ss : value) {
92 if (ss.length() != 3 || ss.charAt(1) != '-')
93 throw illegalArg("Value passed to ranges() must be 3 characters");
94 range(ss.charAt(0), ss.charAt(2));
95 }
96 return this;
97 }
98 }
99
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 }