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