callgraph/src/main/java/org/netbeans/lib/callgraph/util/EightBitStrings.java
author Tim Boudreau <tboudreau@netbeans.org>
Sat, 03 Sep 2016 02:41:36 -0400
changeset 18374 04a79821e760
parent 18373 3527a32a19f0
child 18400 c87c223efe6a
permissions -rw-r--r--
Eliminate duplicates in graph files
tboudreau@18373
     1
/*
tboudreau@18301
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
tboudreau@18301
     3
 *
tboudreau@18301
     4
 * Copyright (C) 1997-2015 Oracle and/or its affiliates. All rights reserved.
tboudreau@18301
     5
 *
tboudreau@18301
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
tboudreau@18301
     7
 * Other names may be trademarks of their respective owners.
tboudreau@18301
     8
 *
tboudreau@18301
     9
 * The contents of this file are subject to the terms of either the GNU
tboudreau@18301
    10
 * General Public License Version 2 only ("GPL") or the Common
tboudreau@18301
    11
 * Development and Distribution License("CDDL") (collectively, the
tboudreau@18301
    12
 * "License"). You may not use this file except in compliance with the
tboudreau@18301
    13
 * License. You can obtain a copy of the License at
tboudreau@18301
    14
 * http://www.netbeans.org/cddl-gplv2.html
tboudreau@18301
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
tboudreau@18301
    16
 * specific language governing permissions and limitations under the
tboudreau@18301
    17
 * License.  When distributing the software, include this License Header
tboudreau@18301
    18
 * Notice in each file and include the License file at
tboudreau@18301
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
tboudreau@18301
    20
 * particular file as subject to the "Classpath" exception as provided
tboudreau@18301
    21
 * by Oracle in the GPL Version 2 section of the License file that
tboudreau@18301
    22
 * accompanied this code. If applicable, add the following below the
tboudreau@18301
    23
 * License Header, with the fields enclosed by brackets [] replaced by
tboudreau@18301
    24
 * your own identifying information:
tboudreau@18301
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
tboudreau@18301
    26
 *
tboudreau@18301
    27
 * Contributor(s):
tboudreau@18301
    28
 *
tboudreau@18301
    29
 * The Original Software is NetBeans. The Initial Developer of the Original
tboudreau@18301
    30
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
tboudreau@18301
    31
 * Microsystems, Inc. All Rights Reserved.
tboudreau@18301
    32
 *
tboudreau@18301
    33
 * If you wish your version of this file to be governed by only the CDDL
tboudreau@18301
    34
 * or only the GPL Version 2, indicate your decision by adding
tboudreau@18301
    35
 * "[Contributor] elects to include this software in this distribution
tboudreau@18301
    36
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
tboudreau@18301
    37
 * single choice of license, a recipient has the option to distribute
tboudreau@18301
    38
 * your version of this file under either the CDDL, the GPL Version 2 or
tboudreau@18301
    39
 * to extend the choice of license to its licensees as provided above.
tboudreau@18301
    40
 * However, if you add GPL Version 2 code and therefore, elected the GPL
tboudreau@18301
    41
 * Version 2 license, then the option applies only if the new code is
tboudreau@18301
    42
 * made subject to such option by the copyright holder.
tboudreau@18301
    43
 */
tboudreau@18301
    44
package org.netbeans.lib.callgraph.util;
tboudreau@18301
    45
tboudreau@18301
    46
import java.io.ByteArrayOutputStream;
tboudreau@18301
    47
import java.io.IOException;
tboudreau@18373
    48
import java.io.UnsupportedEncodingException;
tboudreau@18301
    49
import java.nio.ByteBuffer;
tboudreau@18301
    50
import java.nio.charset.CharacterCodingException;
tboudreau@18301
    51
import java.nio.charset.Charset;
tboudreau@18373
    52
import java.util.ArrayList;
tboudreau@18301
    53
import java.util.Arrays;
tboudreau@18373
    54
import java.util.Collection;
tboudreau@18373
    55
import java.util.Iterator;
tboudreau@18373
    56
import java.util.List;
tboudreau@18373
    57
import java.util.Locale;
tboudreau@18301
    58
tboudreau@18301
    59
/**
tboudreau@18301
    60
 * byte[] backed string to cut memory consumption from Strings in half for most
tboudreau@18301
    61
 * codebases, to reduce memory footprint to process larger codebases by using 8
tboudreau@18301
    62
 * bits per character instead of 16.
tboudreau@18301
    63
 *
tboudreau@18301
    64
 * @author Tim Boudreau
tboudreau@18301
    65
 */
tboudreau@18301
    66
public class EightBitStrings {
tboudreau@18301
    67
tboudreau@18301
    68
    private static final Charset UTF = Charset.forName("UTF-8");
tboudreau@18301
    69
tboudreau@18301
    70
    private final InternTable INTERN_TABLE = new InternTable();
tboudreau@18301
    71
    public final CharSequence DOT = create(".");
tboudreau@18301
    72
    public final CharSequence QUOTE = create("\"");
tboudreau@18374
    73
    public final CharSequence SPACE = create(" ");
tboudreau@18374
    74
    public final CharSequence QUOTE_SPACE = create("\" ");
tboudreau@18301
    75
    public final CharSequence CLOSE_OPEN_QUOTE = create("\" \"");
tboudreau@18301
    76
tboudreau@18374
    77
    private final boolean disabled;
tboudreau@18374
    78
    private final boolean aggressive;
tboudreau@18301
    79
tboudreau@18301
    80
    public EightBitStrings(boolean disabled) {
tboudreau@18374
    81
        this (disabled, false);
tboudreau@18301
    82
    }
tboudreau@18301
    83
tboudreau@18374
    84
    public EightBitStrings(boolean disabled, boolean aggressive) {
tboudreau@18374
    85
        this.disabled = disabled;
tboudreau@18374
    86
        this.aggressive = aggressive;
tboudreau@18374
    87
    }
tboudreau@18374
    88
    
tboudreau@18301
    89
    public void clear() {
tboudreau@18301
    90
        INTERN_TABLE.dispose();
tboudreau@18301
    91
    }
tboudreau@18301
    92
tboudreau@18373
    93
    public ComparableCharSequence create(CharSequence string) {
tboudreau@18301
    94
        if (disabled) {
tboudreau@18373
    95
            return string instanceof ComparableCharSequence ? (ComparableCharSequence) string
tboudreau@18373
    96
                    : new StringWrapper(string.toString());
tboudreau@18301
    97
        }
tboudreau@18374
    98
        if (aggressive) {
tboudreau@18374
    99
            return concat(string);
tboudreau@18374
   100
        }
tboudreau@18301
   101
        return INTERN_TABLE.intern(string);
tboudreau@18301
   102
    }
tboudreau@18301
   103
tboudreau@18373
   104
    public ComparableCharSequence concat(CharSequence... seqs) {
tboudreau@18373
   105
        if (seqs.length == 1 && seqs[0] instanceof ComparableCharSequence) {
tboudreau@18373
   106
            return (ComparableCharSequence) seqs[0];
tboudreau@18373
   107
        }
tboudreau@18301
   108
        if (disabled) {
tboudreau@18301
   109
            StringBuilder sb = new StringBuilder();
tboudreau@18301
   110
            for (CharSequence c : seqs) {
tboudreau@18301
   111
                sb.append(c);
tboudreau@18301
   112
            }
tboudreau@18373
   113
            return new StringWrapper(sb.toString());
tboudreau@18374
   114
        } else if (aggressive) {
tboudreau@18374
   115
            List<CharSequence> nue = new ArrayList<>(seqs.length + (seqs.length / 2));
tboudreau@18374
   116
            for (CharSequence seq : seqs) {
tboudreau@18374
   117
                int ln = seq.length();
tboudreau@18374
   118
                StringBuilder sb = new StringBuilder();
tboudreau@18374
   119
                for(int i=0; i < ln; i++) {
tboudreau@18374
   120
                    char c = seq.charAt(i);
tboudreau@18374
   121
                    if (Character.isLetter(c) || Character.isDigit(c)) {
tboudreau@18374
   122
                        sb.append(c);
tboudreau@18374
   123
                    } else {
tboudreau@18374
   124
                        nue.add(sb.toString());
tboudreau@18374
   125
                        sb = new StringBuilder();
tboudreau@18374
   126
                        nue.add(new String(new char[] { c }));
tboudreau@18374
   127
                    }
tboudreau@18374
   128
                }
tboudreau@18374
   129
                if (sb.length() > 0) {
tboudreau@18374
   130
                    nue.add(sb.toString());
tboudreau@18374
   131
                }
tboudreau@18374
   132
            }
tboudreau@18374
   133
            if (nue.size() != seqs.length) {
tboudreau@18374
   134
                seqs = nue.toArray(new CharSequence[nue.size()]);
tboudreau@18374
   135
            }
tboudreau@18301
   136
        }
tboudreau@18301
   137
        return new Concatenation(seqs);
tboudreau@18301
   138
    }
tboudreau@18301
   139
tboudreau@18374
   140
    public ComparableCharSequence concatQuoted(Collection<Object> seqs) {
tboudreau@18373
   141
        if (disabled) {
tboudreau@18373
   142
            StringBuilder sb = new StringBuilder("\"");
tboudreau@18374
   143
            boolean first = true;
tboudreau@18374
   144
            for (Iterator<Object> it = seqs.iterator(); it.hasNext();) {
tboudreau@18374
   145
                Object c = it.next();
tboudreau@18374
   146
                if (!first) {
tboudreau@18374
   147
                    sb.append(SPACE);
tboudreau@18374
   148
                }
tboudreau@18374
   149
                if (c instanceof CharSequence) {
tboudreau@18374
   150
                    sb.append(QUOTE);
tboudreau@18374
   151
                }
tboudreau@18373
   152
                sb.append(c);
tboudreau@18374
   153
                if (c instanceof CharSequence) {
tboudreau@18374
   154
                    sb.append(QUOTE);
tboudreau@18373
   155
                }
tboudreau@18374
   156
                first = false;
tboudreau@18373
   157
            }
tboudreau@18373
   158
            return new StringWrapper(sb.toString());
tboudreau@18373
   159
        } else {
tboudreau@18374
   160
            List<CharSequence> quoted = new ArrayList<>((seqs.size() * 3) + 1);
tboudreau@18374
   161
            for (Iterator<Object> it = seqs.iterator(); it.hasNext();) {
tboudreau@18374
   162
                Object c = it.next();
tboudreau@18374
   163
                if (c instanceof CharSequence) {
tboudreau@18374
   164
                    quoted.add(QUOTE);
tboudreau@18374
   165
                    quoted.add((CharSequence)c);
tboudreau@18374
   166
                    if (it.hasNext()) {
tboudreau@18374
   167
                        quoted.add(QUOTE_SPACE);
tboudreau@18374
   168
                    } else {
tboudreau@18374
   169
                        quoted.add(QUOTE);
tboudreau@18374
   170
                    }
tboudreau@18373
   171
                } else {
tboudreau@18374
   172
                    quoted.add(create(c.toString()));
tboudreau@18374
   173
                    quoted.add(SPACE);
tboudreau@18373
   174
                }
tboudreau@18373
   175
            }
tboudreau@18373
   176
            return new Concatenation(quoted.toArray(new CharSequence[quoted.size()]));
tboudreau@18373
   177
        }
tboudreau@18373
   178
    }
tboudreau@18373
   179
tboudreau@18301
   180
    private static byte[] toBytes(CharSequence seq) {
tboudreau@18301
   181
        try {
tboudreau@18301
   182
            ByteArrayOutputStream out = new ByteArrayOutputStream();
tboudreau@18301
   183
            out.write(seq.toString().getBytes(UTF));
tboudreau@18301
   184
            return out.toByteArray();
tboudreau@18301
   185
        } catch (IOException ex) {
tboudreau@18301
   186
            throw new IllegalArgumentException(ex);
tboudreau@18301
   187
        }
tboudreau@18301
   188
    }
tboudreau@18301
   189
tboudreau@18301
   190
    private static CharSequence toCharSequence(byte[] bytes) {
tboudreau@18301
   191
        try {
tboudreau@18301
   192
            return UTF.newDecoder().decode(ByteBuffer.wrap(bytes));
tboudreau@18301
   193
        } catch (CharacterCodingException ex) {
tboudreau@18301
   194
            throw new IllegalArgumentException(ex);
tboudreau@18301
   195
        }
tboudreau@18301
   196
    }
tboudreau@18301
   197
tboudreau@18301
   198
    int internTableSize() {
tboudreau@18301
   199
        return INTERN_TABLE.last + 1;
tboudreau@18301
   200
    }
tboudreau@18374
   201
    
tboudreau@18374
   202
    List<CharSequence> dumpInternTable() {
tboudreau@18374
   203
        return INTERN_TABLE.dumpInternTable();
tboudreau@18374
   204
    }
tboudreau@18301
   205
tboudreau@18301
   206
    static class InternTable {
tboudreau@18301
   207
tboudreau@18301
   208
        private static final int SIZE_INCREMENT = 50;
tboudreau@18301
   209
tboudreau@18301
   210
        private int last = -1;
tboudreau@18301
   211
        private Entry[] entries = new Entry[SIZE_INCREMENT];
tboudreau@18301
   212
tboudreau@18301
   213
        void dispose() {
tboudreau@18301
   214
            entries = new Entry[SIZE_INCREMENT];
tboudreau@18301
   215
            last = -1;
tboudreau@18301
   216
        }
tboudreau@18301
   217
tboudreau@18301
   218
        Entry intern(CharSequence seq) {
tboudreau@18301
   219
            if (seq instanceof Entry) {
tboudreau@18301
   220
                return (Entry) seq;
tboudreau@18301
   221
            }
tboudreau@18301
   222
            // We are using an array and binary search to conserve memory
tboudreau@18301
   223
            // here.  This is slower than a HashMap (we sort on insert so
tboudreau@18301
   224
            // we can binary search later), but involves far fewer allocations
tboudreau@18301
   225
            Entry entry = new Entry(toBytes(seq), (short) seq.length());
tboudreau@18301
   226
            int offset = last == -1 ? -1 : Arrays.binarySearch(entries, 0, last + 1, entry);
tboudreau@18301
   227
            if (offset > 0) {
tboudreau@18301
   228
                return entries[offset];
tboudreau@18301
   229
            }
tboudreau@18301
   230
            if (last == entries.length - 1) {
tboudreau@18301
   231
                Entry[] nue = new Entry[entries.length + SIZE_INCREMENT];
tboudreau@18301
   232
                System.arraycopy(entries, 0, nue, 0, entries.length);
tboudreau@18301
   233
                entries = nue;
tboudreau@18301
   234
            }
tboudreau@18301
   235
            entries[++last] = entry;
tboudreau@18301
   236
            Arrays.sort(entries, 0, last + 1);
tboudreau@18301
   237
            return entry;
tboudreau@18301
   238
        }
tboudreau@18374
   239
        
tboudreau@18374
   240
        List<CharSequence> dumpInternTable() {
tboudreau@18374
   241
            return Arrays.asList(entries);
tboudreau@18374
   242
        }
tboudreau@18301
   243
tboudreau@18373
   244
        private static final class Entry implements ComparableCharSequence {
tboudreau@18301
   245
tboudreau@18301
   246
            private final byte[] bytes;
tboudreau@18301
   247
            private final short length;
tboudreau@18301
   248
tboudreau@18301
   249
            public Entry(byte[] bytes, short length) {
tboudreau@18301
   250
                if (length < 0) {
tboudreau@18301
   251
                    throw new Error("String too large");
tboudreau@18301
   252
                }
tboudreau@18301
   253
                this.bytes = bytes;
tboudreau@18301
   254
                this.length = length;
tboudreau@18301
   255
            }
tboudreau@18301
   256
tboudreau@18301
   257
            public int hashCode() {
tboudreau@18301
   258
                int h = 0;
tboudreau@18301
   259
                if (h == 0 && bytes.length > 0) {
tboudreau@18301
   260
                    CharSequence val = toChars();
tboudreau@18301
   261
                    int max = val.length();
tboudreau@18301
   262
                    for (int i = 0; i < max; i++) {
tboudreau@18301
   263
                        h = 31 * h + val.charAt(i);
tboudreau@18301
   264
                    }
tboudreau@18301
   265
                }
tboudreau@18301
   266
                return h;
tboudreau@18301
   267
            }
tboudreau@18301
   268
tboudreau@18301
   269
            @Override
tboudreau@18301
   270
            public boolean equals(Object o) {
tboudreau@18301
   271
                if (o == null) {
tboudreau@18301
   272
                    return false;
tboudreau@18301
   273
                } else if (o == this) {
tboudreau@18301
   274
                    return true;
tboudreau@18301
   275
                } else if (o instanceof Entry) {
tboudreau@18301
   276
                    Entry other = (Entry) o;
tboudreau@18301
   277
                    if (other.bytes.length < bytes.length) {
tboudreau@18301
   278
                        return false;
tboudreau@18301
   279
                    }
tboudreau@18301
   280
                    return Arrays.equals(bytes, other.bytes);
tboudreau@18301
   281
                } else if (o instanceof CharSequence) {
tboudreau@18301
   282
                    return charSequencesEqual(this, (CharSequence) o);
tboudreau@18301
   283
                } else {
tboudreau@18301
   284
                    return false;
tboudreau@18301
   285
                }
tboudreau@18301
   286
            }
tboudreau@18301
   287
tboudreau@18301
   288
            public int compare(Entry o) {
tboudreau@18301
   289
                if (o == this) {
tboudreau@18301
   290
                    return 0;
tboudreau@18301
   291
                }
tboudreau@18301
   292
                int max = Math.min(bytes.length, o.bytes.length);
tboudreau@18301
   293
                for (int i = 0; i < max; i++) {
tboudreau@18301
   294
                    if (bytes[i] > o.bytes[i]) {
tboudreau@18301
   295
                        return 1;
tboudreau@18301
   296
                    } else if (bytes[i] < o.bytes[i]) {
tboudreau@18301
   297
                        return -1;
tboudreau@18301
   298
                    }
tboudreau@18301
   299
                }
tboudreau@18301
   300
                if (bytes.length == o.bytes.length) {
tboudreau@18301
   301
                    return 0;
tboudreau@18301
   302
                } else if (bytes.length > o.bytes.length) {
tboudreau@18301
   303
                    return 1;
tboudreau@18301
   304
                } else {
tboudreau@18301
   305
                    return -1;
tboudreau@18301
   306
                }
tboudreau@18301
   307
            }
tboudreau@18301
   308
tboudreau@18373
   309
            @Override
tboudreau@18301
   310
            public String toString() {
tboudreau@18301
   311
                return new String(bytes, UTF);
tboudreau@18301
   312
            }
tboudreau@18301
   313
tboudreau@18301
   314
            @Override
tboudreau@18301
   315
            public int length() {
tboudreau@18301
   316
                return length;
tboudreau@18301
   317
            }
tboudreau@18301
   318
tboudreau@18301
   319
            CharSequence toChars() {
tboudreau@18301
   320
                return toCharSequence(bytes);
tboudreau@18301
   321
            }
tboudreau@18301
   322
tboudreau@18301
   323
            @Override
tboudreau@18301
   324
            public char charAt(int index) {
tboudreau@18301
   325
                return toCharSequence(bytes).charAt(index);
tboudreau@18301
   326
            }
tboudreau@18301
   327
tboudreau@18301
   328
            @Override
tboudreau@18301
   329
            public CharSequence subSequence(int start, int end) {
tboudreau@18301
   330
                return toCharSequence(bytes).subSequence(start, end);
tboudreau@18301
   331
            }
tboudreau@18301
   332
tboudreau@18301
   333
            @Override
tboudreau@18301
   334
            public int compareTo(CharSequence o) {
tboudreau@18301
   335
                if (o instanceof Entry) {
tboudreau@18301
   336
                    return compare((Entry) o);
tboudreau@18301
   337
                }
tboudreau@18373
   338
                return compareCharSequences(this, o);
tboudreau@18301
   339
            }
tboudreau@18301
   340
        }
tboudreau@18301
   341
    }
tboudreau@18301
   342
tboudreau@18373
   343
    static int compareCharSequences(CharSequence a, CharSequence b) {
tboudreau@18373
   344
        if (a == b) {
tboudreau@18373
   345
            return 0;
tboudreau@18373
   346
        }
tboudreau@18373
   347
        int aLength = a.length();
tboudreau@18373
   348
        int bLength = b.length();
tboudreau@18373
   349
        int max = Math.min(aLength, bLength);
tboudreau@18373
   350
        for (int i = 0; i < max; i++) {
tboudreau@18373
   351
            if (a.charAt(i) > b.charAt(i)) {
tboudreau@18373
   352
                return 1;
tboudreau@18373
   353
            } else if (a.charAt(i) < b.charAt(i)) {
tboudreau@18373
   354
                return -1;
tboudreau@18373
   355
            }
tboudreau@18373
   356
        }
tboudreau@18373
   357
        if (aLength == bLength) {
tboudreau@18373
   358
            return 0;
tboudreau@18373
   359
        } else if (aLength > bLength) {
tboudreau@18373
   360
            return 1;
tboudreau@18373
   361
        } else {
tboudreau@18373
   362
            return -1;
tboudreau@18373
   363
        }
tboudreau@18373
   364
    }
tboudreau@18373
   365
tboudreau@18373
   366
    static boolean debug;
tboudreau@18373
   367
    class Concatenation implements ComparableCharSequence, Comparable<CharSequence> {
tboudreau@18301
   368
tboudreau@18301
   369
        private final InternTable.Entry[] entries;
tboudreau@18301
   370
tboudreau@18301
   371
        Concatenation(CharSequence... entries) {
tboudreau@18374
   372
            List<InternTable.Entry> l = new ArrayList<>(entries.length);
tboudreau@18374
   373
            for (CharSequence cs : entries) {
tboudreau@18374
   374
                if (cs instanceof Concatenation) {
tboudreau@18374
   375
                    Concatenation c1 = (Concatenation) cs;
tboudreau@18374
   376
                    l.addAll(Arrays.asList(c1.entries));
tboudreau@18374
   377
                } else {
tboudreau@18374
   378
                    l.add(INTERN_TABLE.intern(cs));
tboudreau@18374
   379
                }
tboudreau@18301
   380
            }
tboudreau@18374
   381
            this.entries = l.toArray(new InternTable.Entry[l.size()]);
tboudreau@18301
   382
        }
tboudreau@18301
   383
tboudreau@18301
   384
        @Override
tboudreau@18301
   385
        public int length() {
tboudreau@18301
   386
            int result = 0;
tboudreau@18301
   387
            for (int i = 0; i < entries.length; i++) {
tboudreau@18301
   388
                result += entries[i].length;
tboudreau@18301
   389
            }
tboudreau@18301
   390
            return result;
tboudreau@18301
   391
        }
tboudreau@18301
   392
tboudreau@18301
   393
        @Override
tboudreau@18301
   394
        public char charAt(int index) {
tboudreau@18373
   395
            if (entries.length == 0) {
tboudreau@18373
   396
                throw new IndexOutOfBoundsException("0 length but asked for " + index);
tboudreau@18373
   397
            }
tboudreau@18301
   398
            for (int i = 0; i < entries.length; i++) {
tboudreau@18373
   399
                InternTable.Entry e = entries[i];
tboudreau@18373
   400
                if (index >= e.length) {
tboudreau@18373
   401
                    index -= e.length;
tboudreau@18373
   402
                } else {
tboudreau@18373
   403
                    return e.charAt(index);
tboudreau@18301
   404
                }
tboudreau@18301
   405
            }
tboudreau@18373
   406
            throw new IndexOutOfBoundsException(index + " of " + length() + " in " + this + " with entries " + Arrays.asList(entries));
tboudreau@18301
   407
        }
tboudreau@18301
   408
tboudreau@18301
   409
        @Override
tboudreau@18301
   410
        public CharSequence subSequence(int start, int end) {
tboudreau@18301
   411
            return INTERN_TABLE.intern(toString().subSequence(start, end));
tboudreau@18301
   412
        }
tboudreau@18301
   413
tboudreau@18301
   414
        public String toString() {
tboudreau@18301
   415
            StringBuilder sb = new StringBuilder();
tboudreau@18301
   416
            for (InternTable.Entry e : entries) {
tboudreau@18301
   417
                sb.append(e);
tboudreau@18301
   418
            }
tboudreau@18373
   419
            if (debug) {
tboudreau@18374
   420
                sb.append(" - ").append (Arrays.asList(entries));
tboudreau@18373
   421
            }
tboudreau@18301
   422
            return sb.toString();
tboudreau@18301
   423
        }
tboudreau@18301
   424
tboudreau@18301
   425
        private int hash = 0;
tboudreau@18301
   426
tboudreau@18301
   427
        public int hashCode() {
tboudreau@18301
   428
            int h = hash;
tboudreau@18301
   429
            if (h == 0 && length() > 0) {
tboudreau@18301
   430
                for (InternTable.Entry e : entries) {
tboudreau@18301
   431
                    CharSequence chars = e.toChars();
tboudreau@18301
   432
                    int max = chars.length();
tboudreau@18301
   433
                    for (int i = 0; i < max; i++) {
tboudreau@18301
   434
                        h = 31 * h + chars.charAt(i);
tboudreau@18301
   435
                    }
tboudreau@18301
   436
                }
tboudreau@18301
   437
                hash = h;
tboudreau@18301
   438
            }
tboudreau@18301
   439
            return h;
tboudreau@18301
   440
        }
tboudreau@18301
   441
tboudreau@18301
   442
        public boolean equals(Object o) {
tboudreau@18373
   443
            if (true) {
tboudreau@18373
   444
                if (o instanceof CharSequence) {
tboudreau@18373
   445
                    return charSequencesEqual(this, (CharSequence) o);
tboudreau@18373
   446
                }
tboudreau@18373
   447
            }
tboudreau@18301
   448
            if (o == this) {
tboudreau@18301
   449
                return true;
tboudreau@18301
   450
            } else if (o == null) {
tboudreau@18301
   451
                return false;
tboudreau@18301
   452
            } else if (o instanceof Concatenation) {
tboudreau@18301
   453
                Concatenation c = (Concatenation) o;
tboudreau@18301
   454
                if (c.entries.length != entries.length) {
tboudreau@18373
   455
                    return charSequencesEqual(this, c);
tboudreau@18301
   456
                }
tboudreau@18301
   457
                for (int i = 0; i < entries.length; i++) {
tboudreau@18373
   458
                    if (entries[i].length() != entries[i].length) {
tboudreau@18373
   459
                        return charSequencesEqual(this, c);
tboudreau@18373
   460
                    }
tboudreau@18301
   461
                    if (!entries[i].equals(c.entries[i])) {
tboudreau@18301
   462
                        return false;
tboudreau@18301
   463
                    }
tboudreau@18301
   464
                }
tboudreau@18301
   465
                return true;
tboudreau@18301
   466
            } else if (o instanceof CharSequence) {
tboudreau@18301
   467
                return charSequencesEqual(this, (CharSequence) o);
tboudreau@18301
   468
            } else {
tboudreau@18301
   469
                return false;
tboudreau@18301
   470
            }
tboudreau@18301
   471
        }
tboudreau@18373
   472
tboudreau@18373
   473
        @Override
tboudreau@18373
   474
        public int compareTo(CharSequence o) {
tboudreau@18373
   475
            return compareCharSequences(this, o);
tboudreau@18373
   476
        }
tboudreau@18301
   477
    }
tboudreau@18301
   478
tboudreau@18301
   479
    private static boolean charSequencesEqual(CharSequence a, CharSequence b) {
tboudreau@18301
   480
        int maxA = a.length();
tboudreau@18301
   481
        int maxB = b.length();
tboudreau@18301
   482
        if (maxA != maxB) {
tboudreau@18301
   483
            return false;
tboudreau@18301
   484
        }
tboudreau@18301
   485
        for (int i = 0; i < maxA; i++) {
tboudreau@18373
   486
            char aa = a.charAt(i);
tboudreau@18373
   487
            char bb = b.charAt(i);
tboudreau@18373
   488
            if (aa != bb) {
tboudreau@18301
   489
                return false;
tboudreau@18301
   490
            }
tboudreau@18301
   491
        }
tboudreau@18301
   492
        return true;
tboudreau@18301
   493
    }
tboudreau@18373
   494
tboudreau@18373
   495
    static class StringWrapper implements ComparableCharSequence {
tboudreau@18373
   496
tboudreau@18373
   497
        private final String s;
tboudreau@18373
   498
tboudreau@18373
   499
        public StringWrapper(String s) {
tboudreau@18373
   500
            this.s = s;
tboudreau@18373
   501
        }
tboudreau@18373
   502
tboudreau@18373
   503
        public int length() {
tboudreau@18373
   504
            return s.length();
tboudreau@18373
   505
        }
tboudreau@18373
   506
tboudreau@18373
   507
        public boolean isEmpty() {
tboudreau@18373
   508
            return s.isEmpty();
tboudreau@18373
   509
        }
tboudreau@18373
   510
tboudreau@18373
   511
        public char charAt(int index) {
tboudreau@18373
   512
            return s.charAt(index);
tboudreau@18373
   513
        }
tboudreau@18373
   514
tboudreau@18373
   515
        public int codePointAt(int index) {
tboudreau@18373
   516
            return s.codePointAt(index);
tboudreau@18373
   517
        }
tboudreau@18373
   518
tboudreau@18373
   519
        public int codePointBefore(int index) {
tboudreau@18373
   520
            return s.codePointBefore(index);
tboudreau@18373
   521
        }
tboudreau@18373
   522
tboudreau@18373
   523
        public int codePointCount(int beginIndex, int endIndex) {
tboudreau@18373
   524
            return s.codePointCount(beginIndex, endIndex);
tboudreau@18373
   525
        }
tboudreau@18373
   526
tboudreau@18373
   527
        public int offsetByCodePoints(int index, int codePointOffset) {
tboudreau@18373
   528
            return s.offsetByCodePoints(index, codePointOffset);
tboudreau@18373
   529
        }
tboudreau@18373
   530
tboudreau@18373
   531
        public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) {
tboudreau@18373
   532
            s.getChars(srcBegin, srcEnd, dst, dstBegin);
tboudreau@18373
   533
        }
tboudreau@18373
   534
tboudreau@18373
   535
        public void getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin) {
tboudreau@18373
   536
            s.getBytes(srcBegin, srcEnd, dst, dstBegin);
tboudreau@18373
   537
        }
tboudreau@18373
   538
tboudreau@18373
   539
        public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
tboudreau@18373
   540
            return s.getBytes(charsetName);
tboudreau@18373
   541
        }
tboudreau@18373
   542
tboudreau@18373
   543
        public byte[] getBytes(Charset charset) {
tboudreau@18373
   544
            return s.getBytes(charset);
tboudreau@18373
   545
        }
tboudreau@18373
   546
tboudreau@18373
   547
        public byte[] getBytes() {
tboudreau@18373
   548
            return s.getBytes();
tboudreau@18373
   549
        }
tboudreau@18373
   550
tboudreau@18373
   551
        public boolean equals(Object anObject) {
tboudreau@18373
   552
            if (anObject == this) {
tboudreau@18373
   553
                return true;
tboudreau@18373
   554
            }
tboudreau@18373
   555
            if (anObject == null) {
tboudreau@18373
   556
                return false;
tboudreau@18373
   557
            }
tboudreau@18373
   558
            if (!(anObject instanceof CharSequence)) {
tboudreau@18373
   559
                return false;
tboudreau@18373
   560
            }
tboudreau@18373
   561
            return charSequencesEqual(this, (CharSequence) anObject);
tboudreau@18373
   562
        }
tboudreau@18373
   563
tboudreau@18373
   564
        public boolean contentEquals(StringBuffer sb) {
tboudreau@18373
   565
            return s.contentEquals(sb);
tboudreau@18373
   566
        }
tboudreau@18373
   567
tboudreau@18373
   568
        public boolean contentEquals(CharSequence cs) {
tboudreau@18373
   569
            return s.contentEquals(cs);
tboudreau@18373
   570
        }
tboudreau@18373
   571
tboudreau@18373
   572
        public boolean equalsIgnoreCase(String anotherString) {
tboudreau@18373
   573
            return s.equalsIgnoreCase(anotherString);
tboudreau@18373
   574
        }
tboudreau@18373
   575
tboudreau@18373
   576
        public int compareTo(CharSequence anotherString) {
tboudreau@18373
   577
            return compareCharSequences(this, anotherString);
tboudreau@18373
   578
        }
tboudreau@18373
   579
tboudreau@18373
   580
        public int compareToIgnoreCase(String str) {
tboudreau@18373
   581
            return s.compareToIgnoreCase(str);
tboudreau@18373
   582
        }
tboudreau@18373
   583
tboudreau@18373
   584
        public boolean regionMatches(int toffset, String other, int ooffset, int len) {
tboudreau@18373
   585
            return s.regionMatches(toffset, other, ooffset, len);
tboudreau@18373
   586
        }
tboudreau@18373
   587
tboudreau@18373
   588
        public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) {
tboudreau@18373
   589
            return s.regionMatches(ignoreCase, toffset, other, ooffset, len);
tboudreau@18373
   590
        }
tboudreau@18373
   591
tboudreau@18373
   592
        public boolean startsWith(String prefix, int toffset) {
tboudreau@18373
   593
            return s.startsWith(prefix, toffset);
tboudreau@18373
   594
        }
tboudreau@18373
   595
tboudreau@18373
   596
        public boolean startsWith(String prefix) {
tboudreau@18373
   597
            return s.startsWith(prefix);
tboudreau@18373
   598
        }
tboudreau@18373
   599
tboudreau@18373
   600
        public boolean endsWith(String suffix) {
tboudreau@18373
   601
            return s.endsWith(suffix);
tboudreau@18373
   602
        }
tboudreau@18373
   603
tboudreau@18373
   604
        @Override
tboudreau@18373
   605
        public int hashCode() {
tboudreau@18373
   606
            return s.hashCode();
tboudreau@18373
   607
        }
tboudreau@18373
   608
tboudreau@18373
   609
        public int indexOf(int ch) {
tboudreau@18373
   610
            return s.indexOf(ch);
tboudreau@18373
   611
        }
tboudreau@18373
   612
tboudreau@18373
   613
        public int indexOf(int ch, int fromIndex) {
tboudreau@18373
   614
            return s.indexOf(ch, fromIndex);
tboudreau@18373
   615
        }
tboudreau@18373
   616
tboudreau@18373
   617
        public int lastIndexOf(int ch) {
tboudreau@18373
   618
            return s.lastIndexOf(ch);
tboudreau@18373
   619
        }
tboudreau@18373
   620
tboudreau@18373
   621
        public int lastIndexOf(int ch, int fromIndex) {
tboudreau@18373
   622
            return s.lastIndexOf(ch, fromIndex);
tboudreau@18373
   623
        }
tboudreau@18373
   624
tboudreau@18373
   625
        public int indexOf(String str) {
tboudreau@18373
   626
            return s.indexOf(str);
tboudreau@18373
   627
        }
tboudreau@18373
   628
tboudreau@18373
   629
        public int indexOf(String str, int fromIndex) {
tboudreau@18373
   630
            return s.indexOf(str, fromIndex);
tboudreau@18373
   631
        }
tboudreau@18373
   632
tboudreau@18373
   633
        public int lastIndexOf(String str) {
tboudreau@18373
   634
            return s.lastIndexOf(str);
tboudreau@18373
   635
        }
tboudreau@18373
   636
tboudreau@18373
   637
        public int lastIndexOf(String str, int fromIndex) {
tboudreau@18373
   638
            return s.lastIndexOf(str, fromIndex);
tboudreau@18373
   639
        }
tboudreau@18373
   640
tboudreau@18373
   641
        public String substring(int beginIndex) {
tboudreau@18373
   642
            return s.substring(beginIndex);
tboudreau@18373
   643
        }
tboudreau@18373
   644
tboudreau@18373
   645
        public String substring(int beginIndex, int endIndex) {
tboudreau@18373
   646
            return s.substring(beginIndex, endIndex);
tboudreau@18373
   647
        }
tboudreau@18373
   648
tboudreau@18373
   649
        public CharSequence subSequence(int beginIndex, int endIndex) {
tboudreau@18373
   650
            return s.subSequence(beginIndex, endIndex);
tboudreau@18373
   651
        }
tboudreau@18373
   652
tboudreau@18373
   653
        public String concat(String str) {
tboudreau@18373
   654
            return s.concat(str);
tboudreau@18373
   655
        }
tboudreau@18373
   656
tboudreau@18373
   657
        public String replace(char oldChar, char newChar) {
tboudreau@18373
   658
            return s.replace(oldChar, newChar);
tboudreau@18373
   659
        }
tboudreau@18373
   660
tboudreau@18373
   661
        public boolean matches(String regex) {
tboudreau@18373
   662
            return s.matches(regex);
tboudreau@18373
   663
        }
tboudreau@18373
   664
tboudreau@18373
   665
        public boolean contains(CharSequence s) {
tboudreau@18373
   666
            return this.s.contains(s);
tboudreau@18373
   667
        }
tboudreau@18373
   668
tboudreau@18373
   669
        public String replaceFirst(String regex, String replacement) {
tboudreau@18373
   670
            return s.replaceFirst(regex, replacement);
tboudreau@18373
   671
        }
tboudreau@18373
   672
tboudreau@18373
   673
        public String replaceAll(String regex, String replacement) {
tboudreau@18373
   674
            return s.replaceAll(regex, replacement);
tboudreau@18373
   675
        }
tboudreau@18373
   676
tboudreau@18373
   677
        public String replace(CharSequence target, CharSequence replacement) {
tboudreau@18373
   678
            return s.replace(target, replacement);
tboudreau@18373
   679
        }
tboudreau@18373
   680
tboudreau@18373
   681
        public String[] split(String regex, int limit) {
tboudreau@18373
   682
            return s.split(regex, limit);
tboudreau@18373
   683
        }
tboudreau@18373
   684
tboudreau@18373
   685
        public String[] split(String regex) {
tboudreau@18373
   686
            return s.split(regex);
tboudreau@18373
   687
        }
tboudreau@18373
   688
tboudreau@18373
   689
        public static String join(CharSequence delimiter, CharSequence... elements) {
tboudreau@18373
   690
            return String.join(delimiter, elements);
tboudreau@18373
   691
        }
tboudreau@18373
   692
tboudreau@18373
   693
        public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) {
tboudreau@18373
   694
            return String.join(delimiter, elements);
tboudreau@18373
   695
        }
tboudreau@18373
   696
tboudreau@18373
   697
        public String toLowerCase(Locale locale) {
tboudreau@18373
   698
            return s.toLowerCase(locale);
tboudreau@18373
   699
        }
tboudreau@18373
   700
tboudreau@18373
   701
        public String toLowerCase() {
tboudreau@18373
   702
            return s.toLowerCase();
tboudreau@18373
   703
        }
tboudreau@18373
   704
tboudreau@18373
   705
        public String toUpperCase(Locale locale) {
tboudreau@18373
   706
            return s.toUpperCase(locale);
tboudreau@18373
   707
        }
tboudreau@18373
   708
tboudreau@18373
   709
        public String toUpperCase() {
tboudreau@18373
   710
            return s.toUpperCase();
tboudreau@18373
   711
        }
tboudreau@18373
   712
tboudreau@18373
   713
        public String trim() {
tboudreau@18373
   714
            return s.trim();
tboudreau@18373
   715
        }
tboudreau@18373
   716
tboudreau@18373
   717
        public String toString() {
tboudreau@18373
   718
            return s.toString();
tboudreau@18373
   719
        }
tboudreau@18373
   720
tboudreau@18373
   721
        public char[] toCharArray() {
tboudreau@18373
   722
            return s.toCharArray();
tboudreau@18373
   723
        }
tboudreau@18373
   724
tboudreau@18373
   725
        public static String format(String format, Object... args) {
tboudreau@18373
   726
            return String.format(format, args);
tboudreau@18373
   727
        }
tboudreau@18373
   728
tboudreau@18373
   729
        public static String format(Locale l, String format, Object... args) {
tboudreau@18373
   730
            return String.format(l, format, args);
tboudreau@18373
   731
        }
tboudreau@18373
   732
tboudreau@18373
   733
        public static String valueOf(Object obj) {
tboudreau@18373
   734
            return String.valueOf(obj);
tboudreau@18373
   735
        }
tboudreau@18373
   736
tboudreau@18373
   737
        public static String valueOf(char[] data) {
tboudreau@18373
   738
            return String.valueOf(data);
tboudreau@18373
   739
        }
tboudreau@18373
   740
tboudreau@18373
   741
        public static String valueOf(char[] data, int offset, int count) {
tboudreau@18373
   742
            return String.valueOf(data, offset, count);
tboudreau@18373
   743
        }
tboudreau@18373
   744
tboudreau@18373
   745
        public static String copyValueOf(char[] data, int offset, int count) {
tboudreau@18373
   746
            return String.copyValueOf(data, offset, count);
tboudreau@18373
   747
        }
tboudreau@18373
   748
tboudreau@18373
   749
        public static String copyValueOf(char[] data) {
tboudreau@18373
   750
            return String.copyValueOf(data);
tboudreau@18373
   751
        }
tboudreau@18373
   752
tboudreau@18373
   753
        public static String valueOf(boolean b) {
tboudreau@18373
   754
            return String.valueOf(b);
tboudreau@18373
   755
        }
tboudreau@18373
   756
tboudreau@18373
   757
        public static String valueOf(char c) {
tboudreau@18373
   758
            return String.valueOf(c);
tboudreau@18373
   759
        }
tboudreau@18373
   760
tboudreau@18373
   761
        public static String valueOf(int i) {
tboudreau@18373
   762
            return String.valueOf(i);
tboudreau@18373
   763
        }
tboudreau@18373
   764
tboudreau@18373
   765
        public static String valueOf(long l) {
tboudreau@18373
   766
            return String.valueOf(l);
tboudreau@18373
   767
        }
tboudreau@18373
   768
tboudreau@18373
   769
        public static String valueOf(float f) {
tboudreau@18373
   770
            return String.valueOf(f);
tboudreau@18373
   771
        }
tboudreau@18373
   772
tboudreau@18373
   773
        public static String valueOf(double d) {
tboudreau@18373
   774
            return String.valueOf(d);
tboudreau@18373
   775
        }
tboudreau@18373
   776
tboudreau@18373
   777
        public String intern() {
tboudreau@18373
   778
            return s.intern();
tboudreau@18373
   779
        }
tboudreau@18373
   780
tboudreau@18373
   781
    }
tboudreau@18301
   782
}