RBBISymbolTable.java

// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 ***************************************************************************
 *   Copyright (C) 2002-2009 International Business Machines Corporation   *
 *   and others. All rights reserved.                                      *
 ***************************************************************************
 */
package com.ibm.icu.text;

import com.ibm.icu.lang.UCharacter;
import java.text.ParsePosition;
import java.util.HashMap;

class RBBISymbolTable implements SymbolTable {

    HashMap<String, RBBISymbolTableEntry> fHashTable;
    RBBIRuleScanner fRuleScanner;

    static class RBBISymbolTableEntry {
        String key;
        RBBINode val;
    }

    RBBISymbolTable(RBBIRuleScanner rs) {
        fRuleScanner = rs;
        fHashTable = new HashMap<String, RBBISymbolTableEntry>();
    }

    //
    //  RBBISymbolTable::lookup       This function from the abstract symbol table interface
    //                                looks up a variable name and returns a UnicodeString
    //                                containing the substitution text.
    //
    //                                The variable name does NOT include the leading $.
    //
    @Override
    public char[] lookup(String s) {
        final RBBISymbolTableEntry el = fHashTable.get(s);
        if (el == null) {
            return null;
        }

        final RBBINode exprNode = el.val.fLeftChild; // Root node of expression for variable
        // Return the original source string for the expression.
        // Note that for set-valued variables used in UnicodeSet expressions, this would be rejected
        // by the UnicodeSet parser if the source itself contains variable references.  For
        // instance, with
        //     $CaseIgnorable   = [[:Mn:][:Me:][:Cf:][:Lm:][:Sk:] \u0027 \u00AD \u2019];
        //     $Cased = [[:Upper_Case:][:Lower_Case:][:Lt:] - $CaseIgnorable];
        // If lookupSet were not overridden, when parsing the right-hand side of
        //     $NotCased        = [[^ $Cased] - $CaseIgnorable];
        // there would be a call to lookup("Cased") which would return
        //     "[[:Upper_Case:][:Lower_Case:][:Lt:]-$CaseIgnorable]". This contains a variable,
        // which is  disallowed by the UnicodeSet parser inside a variable expansion.
        // However, set-valued variables are pre-parsed, and returned by lookupSet instead, so this
        // call to lookup() never happens; instead, lookupSet("CaseIgnorable") is called when
        // computing $Cased and returns the non-null value of $CaseIgnorable, and then when
        // computing $NotCased, lookupSet("Cased") returns the value computed for $Cased.
        return exprNode.fText.toCharArray();
    }

    @Override
    public UnicodeSet lookupSet(String s) {
        final RBBISymbolTableEntry el = fHashTable.get(s);
        if (el == null) {
            return null;
        }
        final RBBINode exprNode = el.val.fLeftChild;
        if (exprNode.fType == RBBINode.setRef) {
            return exprNode.fLeftChild.fInputSet;
        } else {
            return null;
        }
    }

    // No longer used, see ICU-23297.
    @Override
    public UnicodeMatcher lookupMatcher(int ch) {
        return null;
    }

    //
    // RBBISymbolTable::parseReference   This function from the abstract symbol table interface
    //                                   looks for a $variable name in the source text.
    //                                   It does not look it up, only scans for it.
    //                                   It is used by the UnicodeSet parser.
    //
    @Override
    public String parseReference(String text, ParsePosition pos, int limit) {
        int start = pos.getIndex();
        int i = start;
        String result = "";
        while (i < limit) {
            int c = UTF16.charAt(text, i);
            if ((i == start && !UCharacter.isUnicodeIdentifierStart(c))
                    || !UCharacter.isUnicodeIdentifierPart(c)) {
                break;
            }
            i += UTF16.getCharCount(c);
        }
        if (i == start) { // No valid name chars
            return result; // Indicate failure with empty string
        }
        pos.setIndex(i);
        result = text.substring(start, i);
        return result;
    }

    //
    // RBBISymbolTable::lookupNode      Given a key (a variable name), return the
    //                                  corresponding RBBI Node.  If there is no entry
    //                                  in the table for this name, return NULL.
    //
    RBBINode lookupNode(String key) {

        RBBINode retNode = null;
        RBBISymbolTableEntry el;

        el = fHashTable.get(key);
        if (el != null) {
            retNode = el.val;
        }
        return retNode;
    }

    //
    //    RBBISymbolTable::addEntry     Add a new entry to the symbol table.
    //                                  Indicate an error if the name already exists -
    //                                    this will only occur in the case of duplicate
    //                                    variable assignments.
    //
    void addEntry(String key, RBBINode val) {
        RBBISymbolTableEntry e;
        e = fHashTable.get(key);
        if (e != null) {
            fRuleScanner.error(RBBIRuleBuilder.U_BRK_VARIABLE_REDFINITION);
            return;
        }

        e = new RBBISymbolTableEntry();
        e.key = key;
        e.val = val;
        fHashTable.put(e.key, e);
    }

    //
    //  RBBISymbolTable::print    Debugging function, dump out the symbol table contents.
    //
    void rbbiSymtablePrint() {
        System.out.print(
                "Variable Definitions\n"
                        + "Name               Node Val     String Val\n"
                        + "----------------------------------------------------------------------\n");

        RBBISymbolTableEntry[] syms = fHashTable.values().toArray(new RBBISymbolTableEntry[0]);

        for (int i = 0; i < syms.length; i++) {
            RBBISymbolTableEntry s = syms[i];

            System.out.print("  " + s.key + "  "); // TODO:  format output into columns.
            System.out.print("  " + s.val + "  ");
            System.out.print(s.val.fLeftChild.fText);
            System.out.print("\n");
        }

        System.out.println("\nParsed Variable Definitions\n");
        for (int i = 0; i < syms.length; i++) {
            RBBISymbolTableEntry s = syms[i];
            System.out.print(s.key);
            s.val.fLeftChild.printTree(true);
            System.out.print("\n");
        }
    }
}