| 
001 /*002  * Java Genetic Algorithm Library (jenetics-3.0.0).
 003  * Copyright (c) 2007-2014 Franz Wilhelmstötter
 004  *
 005  * Licensed under the Apache License, Version 2.0 (the "License");
 006  * you may not use this file except in compliance with the License.
 007  * 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  *
 017  * Author:
 018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
 019  */
 020 package org.jenetics.util;
 021
 022 import static java.util.Objects.requireNonNull;
 023 import static org.jenetics.internal.util.Equality.eq;
 024
 025 import java.io.Serializable;
 026 import java.util.Arrays;
 027 import java.util.regex.PatternSyntaxException;
 028
 029 import org.jenetics.internal.collection.ArrayProxyISeq;
 030 import org.jenetics.internal.collection.CharArrayProxy;
 031 import org.jenetics.internal.util.Equality;
 032 import org.jenetics.internal.util.Hash;
 033
 034 /**
 035  * This class is used for holding the valid characters of an
 036  * {@link org.jenetics.CharacterGene}. It is not a character sequence in the
 037  * classical sense. The characters of this sequence are sorted and doesn't
 038  * contain duplicate values, like a set.
 039  *
 040  * [code]
 041  * final CharSeq cs1 = new CharSeq("abcdeaafg");
 042  * final CharSeq cs2 = new CharSeq("gfedcbabb");
 043  * assert(cs1.equals(cs2));
 044  * [/code]
 045  *
 046  * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
 047  * @since 1.0
 048  * @version 2.0 — <em>$Date: 2014-12-28 $</em>
 049  */
 050 public final class CharSeq
 051     extends CharSeqBase
 052     implements
 053         CharSequence,
 054         ISeq<Character>,
 055         Comparable<CharSeq>,
 056         Serializable
 057 {
 058     private static final long serialVersionUID = 2L;
 059
 060     /**
 061      * Create a new (distinct) CharSeq from the given {@code characters}. The
 062      * given {@link CharSequence} is sorted and duplicate values are removed
 063      *
 064      * @see #CharSeq(CharSequence)
 065      *
 066      * @param characters the characters.
 067      * @throws NullPointerException if the {@code characters} are {@code null}.
 068      */
 069     public CharSeq(final char[] characters) {
 070         super(distinct(characters.clone()));
 071     }
 072
 073     /**
 074      * Create a new (distinct) CharSeq from the given {@code characters}. The
 075      * given {@link CharSequence} is sorted and duplicate values are removed.
 076      *
 077      * @param characters the characters.
 078      * @throws NullPointerException if the {@code characters} are {@code null}.
 079      */
 080     public CharSeq(final CharSequence characters) {
 081         this(toCharArray(characters));
 082     }
 083
 084     private static char[] toCharArray(final CharSequence characters) {
 085         requireNonNull(characters, "Characters");
 086
 087         final char[] chars = new char[characters.length()];
 088         for (int i = chars.length; --i >= 0;) {
 089             chars[i] = characters.charAt(i);
 090         }
 091
 092         return chars;
 093     }
 094
 095     private static char[] distinct(final char[] chars) {
 096         Arrays.sort(chars);
 097
 098         int size = 0;
 099         for (int i = 0, j = 0, n = chars.length; i < n && j < n; ++i) {
 100             chars[i] = chars[j];
 101             ++size;
 102
 103             while (j < n && chars[j] == chars[i]) ++j;
 104         }
 105
 106         final char[] array = new char[size];
 107         System.arraycopy(chars, 0, array, 0, size);
 108         return array;
 109     }
 110
 111     @Override
 112     public boolean contains(final Object object) {
 113         return object instanceof Character && contains((Character)object);
 114     }
 115
 116     /**
 117      * Test whether this character set contains the given character {@code c}.
 118      *
 119      * @param c the character to test.
 120      * @return {@code true} if this character set contains the given character,
 121      *          {@code false} otherwise.
 122      * @throws NullPointerException if the given character {@code c} is
 123      *          {@code null}.
 124      */
 125     public boolean contains(final Character c) {
 126         return contains(c.charValue());
 127     }
 128
 129     /**
 130      * Test whether this character set contains the given character {@code c}.
 131      *
 132      * @param c the character to test.
 133      * @return {@code true} if this character set contains the given character,
 134      *          {@code false} otherwise.
 135      */
 136     public boolean contains(final char c) {
 137         return Arrays.binarySearch(proxy.array, c) >= 0;
 138     }
 139
 140     @Override
 141     public char charAt(int index) {
 142         return proxy.array[index];
 143     }
 144
 145     @Override
 146     public int length() {
 147         return proxy.array.length;
 148     }
 149
 150     @Override
 151     public CharSeq subSequence(int start, int end) {
 152         return new CharSeq(new String(proxy.array, start, end - start));
 153     }
 154
 155     /**
 156      * Test whether this character set is empty.
 157      *
 158      * @return {@code true} if this character set is empty, {@code false}
 159      *          otherwise.
 160      */
 161     public boolean isEmpty() {
 162         return proxy.array.length == 0;
 163     }
 164
 165     @Override
 166     public int hashCode() {
 167         return Hash.of(getClass()).and(proxy.array).value();
 168     }
 169
 170     @Override
 171     public boolean equals(final Object obj) {
 172         return Equality.of(this, obj).test(ch -> eq(proxy.array, ch.proxy.array));
 173     }
 174
 175     @Override
 176     public int compareTo(final CharSeq set) {
 177         int result = 0;
 178
 179         final int n = Math.min(proxy.array.length, set.proxy.array.length);
 180         for (int i = 0; i < n && result == 0; ++i) {
 181             result = proxy.array[i] - set.proxy.array[i];
 182         }
 183         if (result == 0) {
 184             result = proxy.array.length - set.proxy.array.length;
 185         }
 186
 187         return result;
 188     }
 189
 190     @Override
 191     public String toString() {
 192         return new String(proxy.array);
 193     }
 194
 195     /**
 196      * Expands the character range for the given {@code pattern}. E.g
 197      * {@code a-zA-Z0-1} will return a string containing all upper and lower
 198      * case characters (from a to z) and all digits form 0 to 9.
 199      *
 200      * @param pattern the {@code pattern} to expand.
 201      * @return the expanded pattern.
 202      * @throws PatternSyntaxException if the pattern could not be expanded.
 203      * @throws NullPointerException if the pattern is {@code null}.
 204      */
 205     public static String expand(final CharSequence pattern) {
 206         requireNonNull(pattern, "Pattern");
 207         final StringBuilder out = new StringBuilder();
 208
 209         for (int i = 0, n = pattern.length(); i < n; ++i) {
 210             if (pattern.charAt(i) == '\\') {
 211                 ++i;
 212                 if (i < pattern.length()) {
 213                     out.append(pattern.charAt(i));
 214                 }
 215             } else if (pattern.charAt(i) == '-') {
 216                 if (i <= 0 || i >= (pattern.length() - 1)) {
 217                     throw new PatternSyntaxException(
 218                         "Dangling range operator '-'", pattern.toString(),
 219                         pattern.length() - 1
 220                     );
 221                 }
 222
 223                 final String range = expand(
 224                     pattern.charAt(i - 1),
 225                     pattern.charAt(i + 1)
 226                 );
 227                 out.append(range);
 228
 229                 ++i;
 230             } else if (i + 1 == n || pattern.charAt(i + 1) != '-') {
 231                 out.append(pattern.charAt(i));
 232             }
 233         }
 234
 235         return out.toString();
 236     }
 237
 238     /**
 239      * Expands the characters between {@code a} and {@code b}.
 240      *
 241      * @param a the start character.
 242      * @param b the stop character.
 243      * @return the expanded characters.
 244      */
 245     public static String expand(final char a, final char b) {
 246         final StringBuilder out = new StringBuilder();
 247
 248         if (a < b) {
 249             char c = a;
 250             while (c <= b) {
 251                 out.append(c);
 252                 c = (char) (c + 1);
 253             }
 254         } else if (a > b) {
 255             char c = a;
 256             while (c >= b) {
 257                 out.append(c);
 258                 c = (char) (c - 1);
 259             }
 260         }
 261
 262         return out.toString();
 263     }
 264
 265     /**
 266      * Expands the character range for the given {@code pattern}. E.g
 267      * {@code a-zA-Z0-1} will return a string containing all upper and lower
 268      * case characters (from a to z) and all digits form 0 to 9.
 269      *
 270      * @see #expand(CharSequence)
 271      *
 272      * @param pattern the {@code pattern} to expand.
 273      * @return the expanded pattern.
 274      * @throws PatternSyntaxException if the pattern could not be expanded.
 275      * @throws NullPointerException if the pattern is {@code null}.
 276      */
 277     public static CharSeq of(final CharSequence pattern) {
 278         return new CharSeq(expand(pattern));
 279     }
 280
 281     /**
 282      * Expands the characters between {@code a} and {@code b}.
 283      *
 284      * @see #expand(char, char)
 285      *
 286      * @param a the start character.
 287      * @param b the stop character.
 288      * @return the expanded characters.
 289      */
 290     public static CharSeq of(final char a, final char b) {
 291         return new CharSeq(expand(a, b));
 292     }
 293
 294     /**
 295      * Helper method for creating a sequence of characters from the given
 296      * {@code CharSequence}. The returned sequence will contain all characters
 297      * in the original order.
 298      *
 299      * @param chars the char sequence to convert.
 300      * @return a sequence which will contain all given chars in the original
 301      *         order.
 302      */
 303     public static ISeq<Character> toISeq(final CharSequence chars) {
 304         final MSeq<Character> seq = MSeq.ofLength(chars.length());
 305         for (int i = 0; i < chars.length(); ++i) {
 306             seq.set(i, chars.charAt(i));
 307         }
 308
 309         return seq.toISeq();
 310     }
 311 }
 312
 313 abstract class CharSeqBase extends ArrayProxyISeq<Character, CharArrayProxy> {
 314     private static final long serialVersionUID = 1L;
 315     protected CharSeqBase(final char[] characters) {
 316         super(new CharArrayProxy(characters, 0, characters.length));
 317     }
 318 }
 |