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}