View Javadoc
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 }