    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  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 */
    017package org.apache.commons.jexl3;
    018
    019import java.util.Collection;
    020import java.util.Collections;
    021import java.util.Set;
    022import java.util.TreeSet;
    023import java.util.Objects;
    024import java.util.function.Predicate;
    025
    026/**
    027 * A set of language feature options.
    028 * These control <em>syntactical</em> constructs that will throw JexlException.Feature exceptions (a
    029 * subclass of JexlException.Parsing) when disabled.
    030 * <ul>
    031 * <li>Registers: register syntax (#number), used internally for {g,s}etProperty
    032 * <li>Reserved Names: a set of reserved variable names that can not be used as local variable (or parameter) names
    033 * <li>Global Side Effect : assigning/modifying values on global variables (=, += , -=, ...)
    034 * <li>Lexical: lexical scope, prevents redefining local variables
    035 * <li>Lexical Shade: local variables shade globals, prevents confusing a global variable with a local one
    036 * <li>Side Effect : assigning/modifying values on any variables or left-value
    037 * <li>Constant Array Reference: ensures array references only use constants;they should be statically solvable.
    038 * <li>New Instance: creating an instance using new(...)
    039 * <li>Loops: loop constructs (while(true), for(...))
    040 * <li>Lambda: function definitions (()-&gt;{...}, function(...) ).
    041 * <li>Method calls: calling methods (obj.method(...) or obj['method'](...)); when disabled, leaves function calls
    042 * - including namespace prefixes - available
    043 * <li>Structured literals: arrays, lists, maps, sets, ranges
    044 * <li>Pragma: pragma construct as in <code>#pragma x y</code>
    045 * <li>Annotation: @annotation statement;
    046 * <li>Thin-arrow: use the thin-arrow, ie <code>-&gt;</code> for lambdas as in <code>x -&gt; x + x</code>
    047 * <li>Fat-arrow: use the  fat-arrow, ie <code>=&gt;</code> for lambdas as in <code>x =&gt; x + x</code>
    048 * <li>Namespace pragma: whether the <code>#pragma jexl.namespace.ns namespace</code> syntax is allowed</li>
    049 * <li>Import pragma: whether the <code>#pragma jexl.import fully.qualified.class.name</code> syntax is allowed</li>
    050 * <li>Comparator names: whether the comparator operator names can be used (as in <code>gt</code> for &gt;,
    051 * <code>lt</code> for &lt;, ...)</li>
    052 * <li>Pragma anywhere: whether pragma, that are <em>not</em> statements and handled before execution begins,
    053 * can appear anywhere in the source or before any statements - ie at the beginning of a script.</li>
    054 * </ul>
    055 * @since 3.2
    056 */
    057public final class JexlFeatures {
    058    /** The feature flags. */
    059    private long flags;
    060    /** The set of reserved names, aka global variables that can not be masked by local variables or parameters. */
    061    private Set<String> reservedNames;
    062    /** The namespace names. */
    063    private Predicate<String> nameSpaces;
    064    /** The false predicate. */
    065    public static final Predicate<String> TEST_STR_FALSE = s->false;
    066    /** Te feature names (for toString()). */
    067    private static final String[] F_NAMES = {
    068        "register", "reserved variable", "local variable", "assign/modify",
    069        "global assign/modify", "array reference", "create instance", "loop", "function",
    070        "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade",
    071        "thin-arrow", "fat-arrow", "namespace pragma", "import pragma", "comparator names", "pragma anywhere"
    072    };
    073    /** Registers feature ordinal. */
    074    private static final int REGISTER = 0;
    075    /** Reserved name feature ordinal. */
    076    public static final int RESERVED = 1;
    077    /** Locals feature ordinal. */
    078    public static final int LOCAL_VAR = 2;
    079    /** Side effects feature ordinal. */
    080    public static final int SIDE_EFFECT = 3;
    081    /** Global side-effects feature ordinal. */
    082    public static final int SIDE_EFFECT_GLOBAL = 4;
    083    /** Array get is allowed on expr. */
    084    public static final int ARRAY_REF_EXPR = 5;
    085    /** New-instance feature ordinal. */
    086    public static final int NEW_INSTANCE = 6;
    087    /** Loops feature ordinal. */
    088    public static final int LOOP = 7;
    089    /** Lambda feature ordinal. */
    090    public static final int LAMBDA = 8;
    091    /** Lambda feature ordinal. */
    092    public static final int METHOD_CALL = 9;
    093    /** Structured literal feature ordinal. */
    094    public static final int STRUCTURED_LITERAL = 10;
    095    /** Pragma feature ordinal. */
    096    public static final int PRAGMA = 11;
    097    /** Annotation feature ordinal. */
    098    public static final int ANNOTATION = 12;
    099    /** Script feature ordinal. */
    100    public static final int SCRIPT = 13;
    101    /** Lexical feature ordinal. */
    102    public static final int LEXICAL = 14;
    103    /** Lexical shade feature ordinal. */
    104    public static final int LEXICAL_SHADE = 15;
    105    /** Fat-arrow lambda syntax. */
    106    public static final int THIN_ARROW = 16;
    107    /** Fat-arrow lambda syntax. */
    108    public static final int FAT_ARROW = 17;
    109    /** Namespace pragma feature ordinal. */
    110    public static final int NS_PRAGMA = 18;
    111    /** Import pragma feature ordinal. */
    112    public static final int IMPORT_PRAGMA = 19;
    113    /** Comparator names (legacy) syntax. */
    114    public static final int COMPARATOR_NAMES = 20;
    115    /** The pragma anywhere feature ordinal. */
    116    public static final int PRAGMA_ANYWHERE = 21;
    117    /**
    118     * The default features flag mask.
    119     */
    120    private static final long DEFAULT_FEATURES =
    121            (1L << LOCAL_VAR)
    122            | (1L << SIDE_EFFECT)
    123            | (1L << SIDE_EFFECT_GLOBAL)
    124            | (1L << ARRAY_REF_EXPR)
    125            | (1L << NEW_INSTANCE)
    126            | (1L << LOOP)
    127            | (1L << LAMBDA)
    128            | (1L << METHOD_CALL)
    129            | (1L << STRUCTURED_LITERAL)
    130            | (1L << PRAGMA)
    131            | (1L << ANNOTATION)
    132            | (1L << SCRIPT)
    133            | (1L << THIN_ARROW)
    134            | (1L << NS_PRAGMA)
    135            | (1L << IMPORT_PRAGMA)
    136            | (1L << COMPARATOR_NAMES)
    137            | (1L << PRAGMA_ANYWHERE);
    138
    139    /**
    140     * Creates an all-features-enabled instance.
    141     */
    142    public JexlFeatures() {
    143        flags = DEFAULT_FEATURES;
    144        reservedNames = Collections.emptySet();
    145        nameSpaces = TEST_STR_FALSE;
    146    }
    147
    148    /**
    149     * Copy constructor.
    150     * @param features the feature to copy from
    151     */
    152    public JexlFeatures(final JexlFeatures features) {
    153        this.flags = features.flags;
    154        this.reservedNames = features.reservedNames;
    155        this.nameSpaces = features.nameSpaces;
    156    }
    157
    158    @Override
    159    public int hashCode() { //CSOFF: MagicNumber
    160        int hash = 3;
    161        hash = 53 * hash + (int) (this.flags ^ (this.flags >>> 32));
    162        hash = 53 * hash + (this.reservedNames != null ? this.reservedNames.hashCode() : 0);
    163        return hash;
    164    }
    165
    166    @Override
    167    public boolean equals(final Object obj) {
    168        if (this == obj) {
    169            return true;
    170        }
    171        if (obj == null) {
    172            return false;
    173        }
    174        if (getClass() != obj.getClass()) {
    175            return false;
    176        }
    177        final JexlFeatures other = (JexlFeatures) obj;
    178        if (this.flags != other.flags) {
    179            return false;
    180        }
    181        if (!Objects.equals(this.reservedNames, other.reservedNames)) {
    182            return false;
    183        }
    184        return true;
    185    }
    186
    187    /**
    188     * The text corresponding to a feature code.
    189     * @param feature the feature number
    190     * @return the feature name
    191     */
    192    public static String stringify(final int feature) {
    193        return feature >= 0 && feature < F_NAMES.length ? F_NAMES[feature] : "unsupported feature";
    194    }
    195
    196    /**
    197     * Sets a collection of reserved names precluding those to be used as local variables or parameter names.
    198     * @param names the names to reserve
    199     * @return this features instance
    200     */
    201    public JexlFeatures reservedNames(final Collection<String> names) {
    202        if (names == null || names.isEmpty()) {
    203            reservedNames = Collections.emptySet();
    204        } else {
    205            reservedNames = Collections.unmodifiableSet(new TreeSet<>(names));
    206        }
    207        setFeature(RESERVED, !reservedNames.isEmpty());
    208        return this;
    209    }
    210
    211    /**
    212     * @return the (unmodifiable) set of reserved names.
    213     */
    214    public Set<String> getReservedNames() {
    215        return reservedNames;
    216    }
    217
    218    /**
    219     * Checks whether a name is reserved.
    220     * @param name the name to check
    221     * @return true if reserved, false otherwise
    222     */
    223    public boolean isReservedName(final String name) {
    224        return name != null && reservedNames.contains(name);
    225    }
    226
    227    /**
    228     * Sets a test to determine namespace declaration.
    229     * @param names the name predicate
    230     * @return this features instance
    231     */
    232    public JexlFeatures namespaceTest(final Predicate<String> names) {
    233        nameSpaces = names == null? TEST_STR_FALSE : names;
    234        return this;
    235    }
    236
    237    /**
    238     * @return the declared namespaces test.
    239     */
    240    public Predicate<String> namespaceTest() {
    241        return nameSpaces;
    242    }
    243
    244    /**
    245     * Sets a feature flag.
    246     * @param feature the feature ordinal
    247     * @param flag    turn-on, turn off
    248     */
    249    private void setFeature(final int feature, final boolean flag) {
    250        if (flag) {
    251            flags |= (1L << feature);
    252        } else {
    253            flags &= ~(1L << feature);
    254        }
    255    }
    256
    257    /**
    258     * Gets a feature flag value.
    259     * @param feature feature ordinal
    260     * @return true if on, false if off
    261     */
    262    private boolean getFeature(final int feature) {
    263        return (flags & (1L << feature)) != 0L;
    264    }
    265
    266    /**
    267     * Sets whether register are enabled.
    268     * <p>
    269     * This is mostly used internally during execution of JexlEngine.{g,s}etProperty.
    270     * <p>
    271     * When disabled, parsing a script/expression using the register syntax will throw a parsing exception.
    272     * @param flag true to enable, false to disable
    273     * @return this features instance
    274     */
    275    public JexlFeatures register(final boolean flag) {
    276        setFeature(REGISTER, flag);
    277        return this;
    278    }
    279
    280    /**
    281     * @return true if register syntax is enabled
    282     */
    283    public boolean supportsRegister() {
    284        return getFeature(REGISTER);
    285    }
    286
    287    /**
    288     * Sets whether local variables are enabled.
    289     * <p>
    290     * When disabled, parsing a script/expression using a local variable or parameter syntax
    291     * will throw a parsing exception.
    292     * @param flag true to enable, false to disable
    293     * @return this features instance
    294     */
    295    public JexlFeatures localVar(final boolean flag) {
    296        setFeature(LOCAL_VAR, flag);
    297        return this;
    298    }
    299
    300    /**
    301     * @return true if local variables syntax is enabled
    302     */
    303    public boolean supportsLocalVar() {
    304        return getFeature(LOCAL_VAR);
    305    }
    306
    307    /**
    308     * Sets whether side effect expressions on global variables (aka non-local) are enabled.
    309     * <p>
    310     * When disabled, parsing a script/expression using syntactical constructs modifying variables
    311     * <em>including all potentially ant-ish variables</em> will throw a parsing exception.
    312     * @param flag true to enable, false to disable
    313     * @return this features instance
    314     */
    315    public JexlFeatures sideEffectGlobal(final boolean flag) {
    316        setFeature(SIDE_EFFECT_GLOBAL, flag);
    317        return this;
    318    }
    319
    320    /**
    321     * @return true if global variables can be assigned
    322     */
    323    public boolean supportsSideEffectGlobal() {
    324        return getFeature(SIDE_EFFECT_GLOBAL);
    325    }
    326
    327    /**
    328     * Sets whether side effect expressions are enabled.
    329     * <p>
    330     * When disabled, parsing a script/expression using syntactical constructs modifying variables
    331     * or members will throw a parsing exception.
    332     * @param flag true to enable, false to disable
    333     * @return this features instance
    334     */
    335    public JexlFeatures sideEffect(final boolean flag) {
    336        setFeature(SIDE_EFFECT, flag);
    337        return this;
    338    }
    339
    340    /**
    341     * @return true if side effects are enabled, false otherwise
    342     */
    343    public boolean supportsSideEffect() {
    344        return getFeature(SIDE_EFFECT);
    345    }
    346
    347    /**
    348     * Sets whether array references expressions are enabled.
    349     * <p>
    350     * When disabled, parsing a script/expression using 'obj[ ref ]' where ref is not a string or integer literal
    351     * will throw a parsing exception;
    352     * @param flag true to enable, false to disable
    353     * @return this features instance
    354     */
    355    public JexlFeatures arrayReferenceExpr(final boolean flag) {
    356        setFeature(ARRAY_REF_EXPR, flag);
    357        return this;
    358    }
    359
    360    /**
    361     * @return true if array references can contain method call expressions, false otherwise
    362     */
    363    public boolean supportsArrayReferenceExpr() {
    364        return getFeature(ARRAY_REF_EXPR);
    365    }
    366
    367    /**
    368     * Sets whether method calls expressions are enabled.
    369     * <p>
    370     * When disabled, parsing a script/expression using 'obj.method()'
    371     * will throw a parsing exception;
    372     * @param flag true to enable, false to disable
    373     * @return this features instance
    374     */
    375    public JexlFeatures methodCall(final boolean flag) {
    376        setFeature(METHOD_CALL, flag);
    377        return this;
    378    }
    379
    380    /**
    381     * @return true if array references can contain expressions, false otherwise
    382     */
    383    public boolean supportsMethodCall() {
    384        return getFeature(METHOD_CALL);
    385    }
    386
    387    /**
    388     * Sets whether array/map/set literal expressions are enabled.
    389     * <p>
    390     * When disabled, parsing a script/expression creating one of these literals
    391     * will throw a parsing exception;
    392     * @param flag true to enable, false to disable
    393     * @return this features instance
    394     */
    395    public JexlFeatures structuredLiteral(final boolean flag) {
    396        setFeature(STRUCTURED_LITERAL, flag);
    397        return this;
    398    }
    399
    400    /**
    401     * @return true if array/map/set literal expressions are supported, false otherwise
    402     */
    403    public boolean supportsStructuredLiteral() {
    404        return getFeature(STRUCTURED_LITERAL);
    405    }
    406
    407    /**
    408     * Sets whether creating new instances is enabled.
    409     * <p>
    410     * When disabled, parsing a script/expression using 'new(...)' will throw a parsing exception;
    411     * using a class as functor will fail at runtime.
    412     * @param flag true to enable, false to disable
    413     * @return this features instance
    414     */
    415    public JexlFeatures newInstance(final boolean flag) {
    416        setFeature(NEW_INSTANCE, flag);
    417        return this;
    418    }
    419
    420    /**
    421     * @return true if creating new instances is enabled, false otherwise
    422     */
    423    public boolean supportsNewInstance() {
    424        return getFeature(NEW_INSTANCE);
    425    }
    426
    427    /**
    428     * Sets whether looping constructs are enabled.
    429     * <p>
    430     * When disabled, parsing a script/expression using syntactic looping constructs (for,while)
    431     * will throw a parsing exception.
    432     * @param flag true to enable, false to disable
    433     * @return this features instance
    434     */
    435    public JexlFeatures loops(final boolean flag) {
    436        setFeature(LOOP, flag);
    437        return this;
    438    }
    439
    440    /**
    441     * @return true if loops are enabled, false otherwise
    442     */
    443    public boolean supportsLoops() {
    444        return getFeature(LOOP);
    445    }
    446
    447    /**
    448     * Sets whether lambda/function constructs are enabled.
    449     * <p>
    450     * When disabled, parsing a script/expression using syntactic lambda constructs (-&gt;,function)
    451     * will throw a parsing exception.
    452     * @param flag true to enable, false to disable
    453     * @return this features instance
    454     */
    455    public JexlFeatures lambda(final boolean flag) {
    456        setFeature(LAMBDA, flag);
    457        return this;
    458    }
    459
    460    /**
    461     * @return true if lambda are enabled, false otherwise
    462     */
    463    public boolean supportsLambda() {
    464        return getFeature(LAMBDA);
    465    }
    466
    467    /**
    468     * Sets whether thin-arrow lambda syntax is enabled.
    469     * <p>
    470     * When disabled, parsing a script/expression using syntactic thin-arrow (-&lt;)
    471     * will throw a parsing exception.
    472     * @param flag true to enable, false to disable
    473     * @return this features instance
    474     * @since 3.3
    475     */
    476    public JexlFeatures thinArrow(final boolean flag) {
    477        setFeature(THIN_ARROW, flag);
    478        return this;
    479    }
    480
    481    /**
    482     * @return true if thin-arrow lambda syntax is enabled, false otherwise
    483     * @since 3.3
    484     */
    485    public boolean supportsThinArrow() {
    486        return getFeature(THIN_ARROW);
    487    }
    488
    489    /**
    490     * Sets whether fat-arrow lambda syntax is enabled.
    491     * <p>
    492     * When disabled, parsing a script/expression using syntactic fat-arrow (=&lt;)
    493     * will throw a parsing exception.
    494     * @param flag true to enable, false to disable
    495     * @return this features instance
    496     * @since 3.3
    497     */
    498    public JexlFeatures fatArrow(final boolean flag) {
    499        setFeature(FAT_ARROW, flag);
    500        return this;
    501    }
    502
    503    /**
    504     * @return true if fat-arrow lambda syntax is enabled, false otherwise
    505     * @since 3.3
    506     */
    507    public boolean supportsFatArrow() {
    508        return getFeature(FAT_ARROW);
    509    }
    510
    511    /**
    512     * Sets whether the legacy comparison operator names syntax is enabled.
    513     * <p>
    514     * When disabled, comparison operators names (eq;ne;le;lt;ge;gt)
    515     * will be treated as plain identifiers.
    516     * @param flag true to enable, false to disable
    517     * @return this features instance
    518     * @since 3.3
    519     */
    520    public JexlFeatures comparatorNames(final boolean flag) {
    521        setFeature(COMPARATOR_NAMES, flag);
    522        return this;
    523    }
    524
    525    /**
    526     * @return true if legacy comparison operator names syntax is enabled, false otherwise
    527     * @since 3.3
    528     */
    529    public boolean supportsComparatorNames() {
    530        return getFeature(COMPARATOR_NAMES);
    531    }
    532
    533    /**
    534     * Sets whether pragma constructs are enabled.
    535     * <p>
    536     * When disabled, parsing a script/expression using syntactic pragma constructs (#pragma)
    537     * will throw a parsing exception.
    538     * @param flag true to enable, false to disable
    539     * @return this features instance
    540     */
    541    public JexlFeatures pragma(final boolean flag) {
    542        setFeature(PRAGMA, flag);
    543        if (!flag) {
    544            setFeature(NS_PRAGMA, false);
    545            setFeature(IMPORT_PRAGMA, false);
    546        }
    547        return this;
    548    }
    549
    550    /**
    551     * @return true if namespace pragma are enabled, false otherwise
    552     */
    553    public boolean supportsPragma() {
    554        return getFeature(PRAGMA);
    555    }
    556
    557    /**
    558     * Sets whether pragma constructs can appear anywhere in the code.
    559     * <p>
    560     * @param flag true to enable, false to disable
    561     * @return this features instance
    562     * @since 3.3
    563     */
    564    public JexlFeatures pragmaAnywhere(final boolean flag) {
    565        setFeature(PRAGMA_ANYWHERE, flag);
    566        return this;
    567    }
    568
    569    /**
    570     * @return true if pragma constructs can appear anywhere in the code, false otherwise
    571     * @since 3.3
    572     */
    573    public boolean supportsPragmaAnywhere() {
    574        return getFeature(PRAGMA_ANYWHERE);
    575    }
    576
    577    /**
    578     * Sets whether namespace pragma constructs are enabled.
    579     * <p>
    580     * When disabled, parsing a script/expression using syntactic namespace pragma constructs
    581     * (#pragma jexl.namespace....) will throw a parsing exception.
    582     * @param flag true to enable, false to disable
    583     * @return this features instance
    584     * @since 3.3
    585     */
    586    public JexlFeatures namespacePragma(final boolean flag) {
    587        setFeature(NS_PRAGMA, flag);
    588        return this;
    589    }
    590
    591    /**
    592     * @return true if namespace pragma are enabled, false otherwise
    593     * @since 3.3
    594     */
    595    public boolean supportsNamespacePragma() {
    596        return getFeature(NS_PRAGMA);
    597    }
    598    /**
    599     * Sets whether import pragma constructs are enabled.
    600     * <p>
    601     * When disabled, parsing a script/expression using syntactic import pragma constructs
    602     * (#pragma jexl.import....) will throw a parsing exception.
    603     * @param flag true to enable, false to disable
    604     * @return this features instance
    605     * @since 3.3
    606     */
    607    public JexlFeatures importPragma(final boolean flag) {
    608        setFeature(IMPORT_PRAGMA, flag);
    609        return this;
    610    }
    611
    612    /**
    613     * @return true if import pragma are enabled, false otherwise
    614     * @since 3.3
    615     */
    616    public boolean supportsImportPragma() {
    617        return getFeature(IMPORT_PRAGMA);
    618    }
    619
    620
    621    /**
    622     * Sets whether annotation constructs are enabled.
    623     * <p>
    624     * When disabled, parsing a script/expression using syntactic annotation constructs (@annotation)
    625     * will throw a parsing exception.
    626     * @param flag true to enable, false to disable
    627     * @return this features instance
    628     */
    629    public JexlFeatures annotation(final boolean flag) {
    630        setFeature(ANNOTATION, flag);
    631        return this;
    632    }
    633
    634    /**
    635     * @return true if annotation are enabled, false otherwise
    636     */
    637    public boolean supportsAnnotation() {
    638        return getFeature(ANNOTATION);
    639    }
    640
    641    /**
    642     * Sets whether scripts constructs are enabled.
    643     * <p>
    644     * When disabled, parsing a script using syntactic script constructs (statements, ...)
    645     * will throw a parsing exception.
    646     * @param flag true to enable, false to disable
    647     * @return this features instance
    648     */
    649    public JexlFeatures script(final boolean flag) {
    650        setFeature(SCRIPT, flag);
    651        return this;
    652    }
    653
    654    /**
    655     * @return true if scripts are enabled, false otherwise
    656     */
    657    public boolean supportsScript() {
    658        return getFeature(SCRIPT);
    659    }
    660
    661    /**
    662     *
    663     * @return true if expressions (aka not scripts) are enabled, false otherwise
    664     */
    665    public boolean supportsExpression() {
    666        return !getFeature(SCRIPT);
    667    }
    668
    669    /**
    670     * Sets whether syntactic lexical mode is enabled.
    671     *
    672     * @param flag true means syntactic lexical function scope is in effect, false implies non-lexical scoping
    673     * @return this features instance
    674     */
    675    public JexlFeatures lexical(final boolean flag) {
    676        setFeature(LEXICAL, flag);
    677        return this;
    678    }
    679
    680
    681    /** @return whether lexical scope feature is enabled */
    682    public boolean isLexical() {
    683        return getFeature(LEXICAL);
    684    }
    685
    686    /**
    687     * Sets whether syntactic lexical shade is enabled.
    688     *
    689     * @param flag true means syntactic lexical shade is in effect and implies lexical scope
    690     * @return this features instance
    691     */
    692    public JexlFeatures lexicalShade(final boolean flag) {
    693        setFeature(LEXICAL_SHADE, flag);
    694        if (flag) {
    695            setFeature(LEXICAL, true);
    696        }
    697        return this;
    698    }
    699
    700
    701    /** @return whether lexical shade feature is enabled */
    702    public boolean isLexicalShade() {
    703        return getFeature(LEXICAL_SHADE);
    704    }
    705}