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.Arrays;
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    * Builder class for {@link AsciiSet} objects.
048    */
049   public static class Builder {
050      final boolean[] store = new boolean[128];
051      
052      /**
053       * Adds a range of characters to this set.
054       * 
055       * @param start The start character.
056       * @param end The end character.
057       * @return This object (for method chaining).
058       */
059      public AsciiSet.Builder range(char start, char end) {
060         for (char c = start; c <= end; c++)
061            if (c < 128)
062               store[c] = true;
063         return this;
064      }
065      
066      /**
067       * Shortcut for calling multiple ranges.
068       * 
069       * @param s Strings of the form "A-Z" where A and Z represent the first and last characters in the range.
070       * @return This object (for method chaining).
071       */
072      public AsciiSet.Builder ranges(String...s) {
073         for (String ss : s) {
074            if (ss.length() != 3 || ss.charAt(1) != '-')
075               throw new RuntimeException("Value passed to ranges() must be 3 characters");
076            range(ss.charAt(0), ss.charAt(2));
077         }
078         return this;
079      }
080      
081      /**
082       * Adds a set of characters to this set.
083       * 
084       * @param chars The characters to keep in this store.
085       * @return This object (for method chaining).
086       */
087      public AsciiSet.Builder chars(String chars) {
088         for (int i = 0; i < chars.length(); i++) {
089            char c = chars.charAt(i);
090            if (c < 128)
091               store[c] = true;
092         }
093         return this;
094      }
095      
096      /**
097       * Create a new {@link AsciiSet} object with the contents of this builder.
098       * 
099       * @return A new {link AsciiSet} object.
100       */
101      public AsciiSet build() {
102         return new AsciiSet(store);
103      }
104   }
105   
106   
107   /**
108    * Returns <jk>true</jk> if the specified character is in this store.
109    * 
110    * @param c The character to check.
111    * @return <jk>true</jk> if the specified character is in this store.
112    */
113   public boolean contains(char c) {
114      if (c > 127)
115         return false;
116      return store[c];
117   }
118
119   /**
120    * Returns <jk>true</jk> if the specified character is in this store.
121    * 
122    * @param c The character to check.
123    * @return <jk>true</jk> if the specified character is in this store.
124    */
125   public boolean contains(int c) {
126      if (c < 0 || c > 127)
127         return false;
128      return store[c];
129   }
130
131   /**
132    * Returns <jk>true</jk> if the specified string contains at least one character in this set.
133    * 
134    * @param s The string to test.
135    * @return <jk>true</jk> if the string is not null and contains at least one character in this set.
136    */
137   public boolean contains(CharSequence s) {
138      if (s == null)
139         return false;
140      for (int i = 0; i < s.length(); i++)
141         if (contains(s.charAt(i)))
142            return true;
143      return false;
144   }
145
146   /**
147    * Returns <jk>true</jk> if the specified string contains only characters in this set.
148    * 
149    * @param s The string to test.
150    * @return 
151    *    <jk>true</jk> if the string contains only characters in this set.
152    *    <br>Nulls always return <jk>false</jk>.
153    *    <br>Blanks always return <jk>true</jk>.
154    */
155   public boolean containsOnly(String s) {
156      if (s == null)
157         return false;
158      for (int i = 0; i < s.length(); i++)
159         if (! contains(s.charAt(i)))
160            return false;
161      return true;
162   }
163}