001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.common.internal;
014
015import java.util.*;
016
017/**
018 * Stores a set of ASCII characters for quick lookup.
019 */
020public final class AsciiSet {
021
022   //-----------------------------------------------------------------------------------------------------------------
023   // Static
024   //-----------------------------------------------------------------------------------------------------------------
025
026   /**
027    * Creates an ASCII set with the specified characters.
028    *
029    * @param chars The characters to keep in this store.
030    * @return A new object.
031    */
032   public static AsciiSet create(String chars) {
033      return new Builder().chars(chars).build();
034   }
035
036   /**
037    * Creates a builder for an ASCII set.
038    *
039    * @return A new builder.
040    */
041   public static AsciiSet.Builder create() {
042      return new Builder();
043   }
044
045   //-----------------------------------------------------------------------------------------------------------------
046   // Builder
047   //-----------------------------------------------------------------------------------------------------------------
048
049   /**
050    * Builder class.
051    */
052   public static class Builder {
053      final boolean[] store = new boolean[128];
054
055      /**
056       * Adds a range of characters to this set.
057       *
058       * @param start The start character.
059       * @param end The end character.
060       * @return This object.
061       */
062      public AsciiSet.Builder range(char start, char end) {
063         for (char c = start; c <= end; c++)
064            if (c < 128)
065               store[c] = true;
066         return this;
067      }
068
069      /**
070       * Shortcut for calling multiple ranges.
071       *
072       * @param s Strings of the form "A-Z" where A and Z represent the first and last characters in the range.
073       * @return This object.
074       */
075      public AsciiSet.Builder ranges(String...s) {
076         for (String ss : s) {
077            if (ss.length() != 3 || ss.charAt(1) != '-')
078               throw new IllegalArgumentException("Value passed to ranges() must be 3 characters");
079            range(ss.charAt(0), ss.charAt(2));
080         }
081         return this;
082      }
083
084      /**
085       * Adds a set of characters to this set.
086       *
087       * @param chars The characters to keep in this store.
088       * @return This object.
089       */
090      public AsciiSet.Builder chars(String chars) {
091         for (int i = 0; i < chars.length(); i++) {
092            char c = chars.charAt(i);
093            if (c < 128)
094               store[c] = true;
095         }
096         return this;
097      }
098
099      /**
100       * Adds a set of characters to this set.
101       *
102       * @param chars The characters to keep in this store.
103       * @return This object.
104       */
105      public Builder chars(char...chars) {
106         for (int i = 0; i < chars.length; i++)
107            if (chars[i] < 128)
108               store[chars[i]] = true;
109         return this;
110      }
111
112      /**
113       * Create a new {@link AsciiSet} object with the contents of this builder.
114       *
115       * @return A new {link AsciiSet} object.
116       */
117      public AsciiSet build() {
118         return new AsciiSet(store);
119      }
120   }
121
122   //-----------------------------------------------------------------------------------------------------------------
123   // Instance
124   //-----------------------------------------------------------------------------------------------------------------
125
126   private final boolean[] store;
127
128   AsciiSet(boolean[] store) {
129      this.store = Arrays.copyOf(store, store.length);
130   }
131
132   /**
133    * Copies an existing {@link AsciiSet} so that you can augment it with additional values.
134    *
135    * @return A builder initialized to the same characters in the copied set.
136    */
137   public AsciiSet.Builder copy() {
138      Builder b = new Builder();
139      for (int i = 0; i < 128; i++)
140         b.store[i] = store[i];
141      return b;
142   }
143
144
145   /**
146    * Returns <jk>true</jk> if the specified character is in this store.
147    *
148    * @param c The character to check.
149    * @return <jk>true</jk> if the specified character is in this store.
150    */
151   public boolean contains(char c) {
152      if (c > 127)
153         return false;
154      return store[c];
155   }
156
157   /**
158    * Returns <jk>true</jk> if the specified character is in this store.
159    *
160    * @param c The character to check.
161    * @return <jk>true</jk> if the specified character is in this store.
162    */
163   public boolean contains(int c) {
164      if (c < 0 || c > 127)
165         return false;
166      return store[c];
167   }
168
169   /**
170    * Returns <jk>true</jk> if the specified string contains at least one character in this set.
171    *
172    * @param s The string to test.
173    * @return <jk>true</jk> if the string is not null and contains at least one character in this set.
174    */
175   public boolean contains(CharSequence s) {
176      if (s == null)
177         return false;
178      for (int i = 0; i < s.length(); i++)
179         if (contains(s.charAt(i)))
180            return true;
181      return false;
182   }
183
184   /**
185    * Returns <jk>true</jk> if the specified string contains only characters in this set.
186    *
187    * @param s The string to test.
188    * @return
189    *    <jk>true</jk> if the string contains only characters in this set.
190    *    <br>Nulls always return <jk>false</jk>.
191    *    <br>Blanks always return <jk>true</jk>.
192    */
193   public boolean containsOnly(String s) {
194      if (s == null)
195         return false;
196      for (int i = 0; i < s.length(); i++)
197         if (! contains(s.charAt(i)))
198            return false;
199      return true;
200   }
201}