1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.xml.catalog;
  26 
  27 import java.net.MalformedURLException;
  28 import java.net.URISyntaxException;
  29 import java.util.HashMap;
  30 import java.util.Map;
  31 import jdk.xml.internal.SecuritySupport;
  32 
  33 /**
  34  * The CatalogFeatures holds a collection of features and properties.
  35  *
  36  *
  37  * <table class="plain" id="CatalogFeatures">
  38  * <caption>Catalog Features</caption>
  39  * <thead>
  40  * <tr>
  41  * <th scope="col" rowspan="2">Feature</th>
  42  * <th scope="col" rowspan="2">Description</th>
  43  * <th scope="col" rowspan="2">Property Name</th>
  44  * <th scope="col" rowspan="2">System Property [1]</th>
  45  * <th scope="col" rowspan="2">jaxp.properties [1]</th>
  46  * <th scope="col" colspan="2" style="text-align:center">Value [2]</th>
  47  * <th scope="col" rowspan="2">Action</th>
  48  * </tr>
  49  * <tr>
  50  * <th scope="col">Type</th>
  51  * <th scope="col">Value</th>
  52  * </tr>
  53  * </thead>
  54  *
  55  * <tbody>
  56  *
  57  * <tr>
  58  * <th scope="row" style="font-weight:normal" id="FILES">FILES</th>
  59  * <td>A semicolon-delimited list of URIs to locate the catalog files.
  60  * The URIs must be absolute and have a URL protocol handler for the URI scheme.
  61  * </td>
  62  * <td>javax.xml.catalog.files</td>
  63  * <td>javax.xml.catalog.files</td>
  64  * <td>javax.xml.catalog.files</td>
  65  * <td>String</td>
  66  * <th id="URIs" scope="row" style="font-weight:normal">URIs</th>
  67  * <td>
  68  * Reads the first catalog as the current catalog; Loads others if no match
  69  * is found in the current catalog including delegate catalogs if any.
  70  * </td>
  71  * </tr>
  72  *
  73  * <tr>
  74  * <th rowspan="2" scope="row" style="font-weight:normal" id="PREFER">PREFER</th>
  75  * <td rowspan="2">Indicates the preference between the public and system
  76  * identifiers. The default value is public [3].</td>
  77  * <td rowspan="2">javax.xml.catalog.prefer</td>
  78  * <td rowspan="2">N/A</td>
  79  * <td rowspan="2">N/A</td>
  80  * <td rowspan="2">String</td>
  81  * <th scope="row" id="system" style="font-weight:normal">{@code system}</th>
  82  * <td>
  83  * Searches system entries for a match; Searches public entries when
  84  * external identifier specifies only a public identifier</td>
  85  * </tr>
  86  * <tr>
  87  * <th scope="row" id="public" style="font-weight:normal">{@code public}</th>
  88  * <td>
  89  * Searches system entries for a match; Searches public entries when
  90  * there is no matching system entry.</td>
  91  * </tr>
  92  *
  93  * <tr>
  94  * <th rowspan="2" scope="row" style="font-weight:normal" id="DEFER">DEFER</th>
  95  * <td rowspan="2">Indicates that the alternative catalogs including those
  96  * specified in delegate entries or nextCatalog are not read until they are
  97  * needed. The default value is true.</td>
  98  * <td rowspan="2">javax.xml.catalog.defer [4]</td>
  99  * <td rowspan="2">javax.xml.catalog.defer</td>
 100  * <td rowspan="2">javax.xml.catalog.defer</td>
 101  * <td rowspan="2">String</td>
 102  * <th scope="row" id="true" style="font-weight:normal">{@code true}</th>
 103  * <td>
 104  * Loads alternative catalogs as needed.
 105  * </td>
 106  * </tr>
 107  * <tr>
 108  * <th scope="row" id="false" style="font-weight:normal">{@code false}</th>
 109  * <td>
 110  * Loads all catalogs[5]. </td>
 111  * </tr>
 112  *
 113  * <tr>
 114  * <th rowspan="3" scope="row" style="font-weight:normal" id="RESOLVE">RESOLVE</th>
 115  * <td rowspan="3">Determines the action if there is no matching entry found after
 116  * all of the specified catalogs are exhausted. The default is strict.</td>
 117  * <td rowspan="3">javax.xml.catalog.resolve [4]</td>
 118  * <td rowspan="3">javax.xml.catalog.resolve</td>
 119  * <td rowspan="3">javax.xml.catalog.resolve</td>
 120  * <td rowspan="3">String</td>
 121  * <th scope="row" id="strict" style="font-weight:normal">{@code strict}</th>
 122  * <td>
 123  * Throws CatalogException if there is no match.
 124  * </td>
 125  * </tr>
 126  * <tr>
 127  * <th scope="row" id="continue" style="font-weight:normal">{@code continue}</th>
 128  * <td>
 129  * Allows the XML parser to continue as if there is no match.
 130  * </td>
 131  * </tr>
 132  * <tr>
 133  * <th scope="row" id="ignore" style="font-weight:normal">{@code ignore}</th>
 134  * <td>
 135  * Tells the XML parser to skip the external references if there no match.
 136  * </td>
 137  * </tr>
 138  *
 139  * </tbody>
 140  * </table>
 141  * <p>
 142  * <b>[1]</b> There is no System property for the features that marked as "N/A".
 143  *
 144  * <p>
 145  * <b>[2]</b> The value shall be exactly as listed in this table, case-sensitive.
 146  * Any unspecified value will result in {@link IllegalArgumentException}.
 147  * <p>
 148  * <b>[3]</b> The Catalog specification defined complex rules on
 149  * <a href="https://www.oasis-open.org/committees/download.php/14809/xml-catalogs.html#attrib.prefer">
 150  * the prefer attribute</a>. Although the prefer can be public or system, the
 151  * specification actually made system the preferred option, that is, no matter
 152  * the option, a system entry is always used if found. Public entries are only
 153  * considered if the prefer is public and system entries are not found. It is
 154  * therefore recommended that the prefer attribute be set as public
 155  * (which is the default).
 156  * <p>
 157  * <b>[4]</b> Although non-standard attributes in the OASIS Catalog specification,
 158  * {@code defer} and {@code resolve} are recognized by the Java Catalog API the
 159  * same as the {@code prefer} as being an attribute in the catalog entry of the
 160  * main catalog. Note that only the attributes specified for the catalog entry
 161  * of the main Catalog file will be used.
 162   * <p>
 163  * <b>[5]</b> If the intention is to share an entire catalog store, it may be desirable to
 164  * set the property {@code javax.xml.catalog.defer} to false to allow the entire
 165  * catalog to be pre-loaded.
 166  *
 167  * <h3>Scope and Order</h3>
 168  * Features and properties can be set through the catalog file, the Catalog API,
 169  * system properties, and {@code jaxp.properties}, with a preference in the same order.
 170  * <p>
 171  * Properties that are specified as attributes in the catalog file for the
 172  * catalog and group entries shall take preference over any of the other settings.
 173  * For example, if a {@code prefer} attribute is set in the catalog file as in
 174  * {@code <catalog prefer="public">}, any other input for the "prefer" property
 175  * is not necessary or will be ignored.
 176  * <p>
 177  * Properties set through the Catalog API override those that may have been set
 178  * by system properties and/or in {@code jaxp.properties}. In case of multiple
 179  * interfaces, the latest in a procedure shall take preference. For
 180  * {@link Feature#FILES}, this means that the URI(s) specified through the methods
 181  * of the {@link CatalogManager} will override any that may have been entered
 182  * through the {@link Builder}.
 183  *
 184  * <p>
 185  * System properties when set shall override those in {@code jaxp.properties}.
 186  * <p>
 187  * The {@code jaxp.properties} file is typically in the conf directory of the Java
 188  * installation. The file is read only once by the JAXP implementation and
 189  * its values are then cached for future use. If the file does not exist
 190  * when the first attempt is made to read from it, no further attempts are
 191  * made to check for its existence. It is not possible to change the value
 192  * of any properties in {@code jaxp.properties} after it has been read.
 193  * <p>
 194  * A CatalogFeatures instance can be created through its builder as illustrated
 195  * in the following sample code:
 196  * <pre>{@code
 197                 CatalogFeatures f = CatalogFeatures.builder()
 198                         .with(Feature.FILES, "file:///etc/xml/catalog")
 199                         .with(Feature.PREFER, "public")
 200                         .with(Feature.DEFER, "true")
 201                         .with(Feature.RESOLVE, "ignore")
 202                         .build();
 203  * }</pre>
 204  *
 205  * <h3>JAXP XML Processor Support</h3>
 206  * The Catalog Features are supported throughout the JAXP processors, including
 207  * SAX and DOM ({@link javax.xml.parsers}), and StAX parsers ({@link javax.xml.stream}),
 208  * Schema Validation ({@link javax.xml.validation}), and XML Transformation
 209  * ({@link javax.xml.transform}). The features described above can be set through JAXP
 210  * factories or processors that define a setProperty or setAttribute interface.
 211  * For example, the following code snippet sets a URI to a catalog file on a SAX
 212  * parser through the {@code javax.xml.catalog.files} property:
 213  *
 214  * <pre>{@code
 215  *      SAXParserFactory spf = SAXParserFactory.newInstance();
 216  *      spf.setFeature(XMLConstants.USE_CATALOG, true); [1]
 217  *      SAXParser parser = spf.newSAXParser();
 218  *      parser.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), "file:///etc/xml/catalog");
 219  * }</pre>
 220  * <p>
 221  * [1] Note that this statement is not required since the default value of
 222  * {@link javax.xml.XMLConstants#USE_CATALOG USE_CATALOG} is true.
 223  *
 224  * <p>
 225  * The JAXP Processors' support for Catalog depends on both the
 226  * {@link javax.xml.XMLConstants#USE_CATALOG USE_CATALOG} feature and the
 227  * existence of valid Catalog file(s). A JAXP processor will use the Catalog
 228  * only when the feature is true and valid Catalog file(s) are specified through
 229  * the {@code javax.xml.catalog.files} property. It will make no attempt to use
 230  * the Catalog if either {@link javax.xml.XMLConstants#USE_CATALOG USE_CATALOG}
 231  * is set to false, or there is no Catalog file specified.
 232  *
 233  * <p>
 234  * The JAXP processors will observe the default settings of the
 235  * {@link javax.xml.catalog.CatalogFeatures}. The processors, for example, will
 236  * report an Exception by default when no matching entry is found since the
 237  * default value of the {@code javax.xml.catalog.resolve} property is strict.
 238  *
 239  * <p>
 240  * The JAXP processors give preference to user-specified custom resolvers. If such
 241  * a resolver is registered, it will be used over the CatalogResolver. If it returns
 242  * null however, the processors will continue resolving with the CatalogResolver.
 243  * If it returns an empty source, no attempt will be made by the CatalogResolver.
 244  *
 245  * <p>
 246  * The Catalog support is available for any process in the JAXP library that
 247  * supports a resolver. The following table lists all such processes.
 248  *
 249  * <h3><a id="ProcessesWithCatalogSupport">Processes with Catalog Support</a></h3>
 250  *
 251  * <table class="striped">
 252  * <caption>Processes with Catalog Support</caption>
 253  * <thead>
 254  * <tr>
 255  * <th scope="col">Process</th>
 256  * <th scope="col">Catalog Entry Type</th>
 257  * <th scope="col">Example</th>
 258  * </tr>
 259  * </thead>
 260  * <tbody>
 261  * <tr>
 262  * <th scope="row">DTDs and external entities</th>
 263  * <td>public, system</td>
 264  * <td>
 265  * <pre>{@literal
 266    The following DTD reference:
 267    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 268 
 269    Can be resolved using the following Catalog entry:
 270    <public publicId="-//W3C//DTD XHTML 1.0 Strict//EN" uri="catalog/xhtml1-strict.dtd"/>
 271    or
 272    <systemSuffix systemIdSuffix="html1-strict.dtd" uri="catalog/xhtml1-strict.dtd"/>
 273  * }</pre>
 274  * </td>
 275  * </tr>
 276  * <tr>
 277  * <th scope="row">XInclude</th>
 278  * <td>uri</td>
 279  * <td>
 280  * <pre>{@literal
 281    The following XInclude element:
 282    <xi:include href="http://openjdk.java.net/xml/disclaimer.xml"/>
 283 
 284    can be resolved using a URI entry:
 285    <uri name="http://openjdk.java.net/xml/disclaimer.xml" uri="file:///pathto/local/disclaimer.xml"/>
 286    or
 287    <uriSuffix uriSuffix="disclaimer.xml" uri="file:///pathto/local/disclaimer.xml"/>
 288  * }</pre>
 289  * </td>
 290  * </tr>
 291  * <tr>
 292  * <th scope="row">XSD import</th>
 293  * <td>uri</td>
 294  * <td>
 295  * <pre>{@literal
 296    The following import element:
 297     <xsd:import namespace="http://openjdk.java.net/xsd/XSDImport_person"
 298                 schemaLocation="http://openjdk.java.net/xsd/XSDImport_person.xsd"/>
 299 
 300    can be resolved using a URI entry:
 301    <uri name="http://openjdk.java.net/xsd/XSDImport_person.xsd" uri="file:///pathto/local/XSDImport_person.xsd"/>
 302    or
 303    <uriSuffix uriSuffix="XSDImport_person.xsd" uri="file:///pathto/local/XSDImport_person.xsd"/>
 304    or
 305    <uriSuffix uriSuffix="http://openjdk.java.net/xsd/XSDImport_person" uri="file:///pathto/local/XSDImport_person.xsd"/>
 306  * }</pre>
 307  * </td>
 308  * </tr>
 309  * <tr>
 310  * <th scope="row">XSD include</th>
 311  * <td>uri</td>
 312  * <td>
 313  * <pre>{@literal
 314    The following include element:
 315    <xsd:include schemaLocation="http://openjdk.java.net/xsd/XSDInclude_person.xsd"/>
 316 
 317    can be resolved using a URI entry:
 318    <uri name="http://openjdk.java.net/xsd/XSDInclude_person.xsd" uri="file:///pathto/local/XSDInclude_person.xsd"/>
 319    or
 320    <uriSuffix uriSuffix="XSDInclude_person.xsd" uri="file:///pathto/local/XSDInclude_person.xsd"/>
 321  * }</pre>
 322  * </td>
 323  * </tr>
 324  * <tr>
 325  * <th scope="row">XSL import and include</th>
 326  * <td>uri</td>
 327  * <td>
 328  * <pre>{@literal
 329    The following include element:
 330    <xsl:include href="http://openjdk.java.net/xsl/include.xsl"/>
 331 
 332    can be resolved using a URI entry:
 333    <uri name="http://openjdk.java.net/xsl/include.xsl" uri="file:///pathto/local/include.xsl"/>
 334    or
 335    <uriSuffix uriSuffix="include.xsl" uri="file:///pathto/local/include.xsl"/>
 336  * }</pre>
 337  * </td>
 338  * </tr>
 339  * <tr>
 340  * <th scope="row">XSL document function</th>
 341  * <td>uri</td>
 342  * <td>
 343  * <pre>{@literal
 344    The document in the following element:
 345    <xsl:variable name="dummy" select="document('http://openjdk.java.net/xsl/list.xml')"/>
 346 
 347    can be resolved using a URI entry:
 348    <uri name="http://openjdk.java.net/xsl/list.xml" uri="file:///pathto/local/list.xml"/>
 349    or
 350    <uriSuffix uriSuffix="list.xml" uri="file:///pathto/local/list.xml"/>
 351  * }</pre>
 352  * </td>
 353  * </tr>
 354  * </tbody>
 355  * </table>
 356  *
 357  * @since 9
 358  */
 359 public class CatalogFeatures {
 360 
 361     /**
 362      * The constant name of the javax.xml.catalog.files property as described
 363      * in the property table above.
 364      */
 365     static final String CATALOG_FILES = "javax.xml.catalog.files";
 366 
 367     /**
 368      * The javax.xml.catalog.prefer property as described
 369      * in the property table above.
 370      */
 371     static final String CATALOG_PREFER = "javax.xml.catalog.prefer";
 372 
 373     /**
 374      * The javax.xml.catalog.defer property as described
 375      * in the property table above.
 376      */
 377     static final String CATALOG_DEFER = "javax.xml.catalog.defer";
 378 
 379     /**
 380      * The javax.xml.catalog.resolve property as described
 381      * in the property table above.
 382      */
 383     static final String CATALOG_RESOLVE = "javax.xml.catalog.resolve";
 384 
 385     //values for the prefer property
 386     static final String PREFER_SYSTEM = "system";
 387     static final String PREFER_PUBLIC = "public";
 388 
 389     //values for the defer property
 390     static final String DEFER_TRUE = "true";
 391     static final String DEFER_FALSE = "false";
 392 
 393     //values for the Resolve property
 394     static final String RESOLVE_STRICT = "strict";
 395     static final String RESOLVE_CONTINUE = "continue";
 396     static final String RESOLVE_IGNORE = "ignore";
 397 
 398     /**
 399      * A Feature type as defined in the
 400      * <a href="CatalogFeatures.html#CatalogFeatures">Catalog Features table</a>.
 401      */
 402     public static enum Feature {
 403         /**
 404          * The {@code javax.xml.catalog.files} property as described in
 405          * item <a href="CatalogFeatures.html#FILES">FILES</a> of the
 406          * Catalog Features table.
 407          */
 408         FILES(CATALOG_FILES, null, true),
 409         /**
 410          * The {@code javax.xml.catalog.prefer} property as described in
 411          * item <a href="CatalogFeatures.html#PREFER">PREFER</a> of the
 412          * Catalog Features table.
 413          */
 414         PREFER(CATALOG_PREFER, PREFER_PUBLIC, false),
 415         /**
 416          * The {@code javax.xml.catalog.defer} property as described in
 417          * item <a href="CatalogFeatures.html#DEFER">DEFER</a> of the
 418          * Catalog Features table.
 419          */
 420         DEFER(CATALOG_DEFER, DEFER_TRUE, true),
 421         /**
 422          * The {@code javax.xml.catalog.resolve} property as described in
 423          * item <a href="CatalogFeatures.html#RESOLVE">RESOLVE</a> of the
 424          * Catalog Features table.
 425          */
 426         RESOLVE(CATALOG_RESOLVE, RESOLVE_STRICT, true);
 427 
 428         private final String name;
 429         private final String defaultValue;
 430         private String value;
 431         private final boolean hasSystem;
 432 
 433         /**
 434          * Constructs a CatalogFeature instance.
 435          * @param name the name of the feature
 436          * @param value the value of the feature
 437          * @param hasSystem a flag to indicate whether the feature is supported
 438          * with a System property
 439          */
 440         Feature(String name, String value, boolean hasSystem) {
 441             this.name = name;
 442             this.defaultValue = value;
 443             this.hasSystem = hasSystem;
 444         }
 445 
 446         /**
 447          * Checks whether the specified property is equal to the current property.
 448          * @param propertyName the name of a property
 449          * @return true if the specified property is the current property, false
 450          * otherwise
 451          */
 452         boolean equalsPropertyName(String propertyName) {
 453             return name.equals(propertyName);
 454         }
 455 
 456         /**
 457          * Returns the name of the corresponding System Property.
 458          *
 459          * @return the name of the System Property
 460          */
 461         public String getPropertyName() {
 462             return name;
 463         }
 464 
 465         /**
 466          * Returns the default value of the property.
 467          * @return the default value of the property
 468          */
 469         public String defaultValue() {
 470             return defaultValue;
 471         }
 472 
 473         /**
 474          * Returns the value of the property.
 475          * @return the value of the property
 476          */
 477         String getValue() {
 478             return value;
 479         }
 480 
 481         /**
 482          * Checks whether System property is supported for the feature.
 483          * @return true it is supported, false otherwise
 484          */
 485         boolean hasSystemProperty() {
 486             return hasSystem;
 487         }
 488     }
 489 
 490     /**
 491      * States of the settings of a property, in the order: default value,
 492      * jaxp.properties file, jaxp system properties, and jaxp api properties
 493      */
 494     static enum State {
 495         /** represents the default state of a feature. */
 496         DEFAULT("default"),
 497         /** indicates the value of the feature is read from jaxp.properties. */
 498         JAXPDOTPROPERTIES("jaxp.properties"),
 499         /** indicates the value of the feature is read from its System property. */
 500         SYSTEMPROPERTY("system property"),
 501         /** indicates the value of the feature is specified through the API. */
 502         APIPROPERTY("property"),
 503         /** indicates the value of the feature is specified as a catalog attribute. */
 504         CATALOGATTRIBUTE("catalog attribute");
 505 
 506         final String literal;
 507 
 508         State(String literal) {
 509             this.literal = literal;
 510         }
 511 
 512         String literal() {
 513             return literal;
 514         }
 515     }
 516 
 517     /**
 518      * Values of the properties
 519      */
 520     private String[] values;
 521 
 522     /**
 523      * States of the settings for each property
 524      */
 525     private State[] states;
 526 
 527     /**
 528      * Private class constructor
 529      */
 530     private CatalogFeatures() {
 531     }
 532 
 533     /**
 534      * Returns a CatalogFeatures instance with default settings.
 535      * @return a default CatalogFeatures instance
 536      */
 537     public static CatalogFeatures defaults() {
 538         return CatalogFeatures.builder().build();
 539     }
 540 
 541     /**
 542      * Constructs a new CatalogFeatures instance with the builder.
 543      *
 544      * @param builder the builder to build the CatalogFeatures
 545      */
 546     CatalogFeatures(Builder builder) {
 547         init();
 548         setProperties(builder);
 549     }
 550 
 551     /**
 552      * Returns the value of the specified feature.
 553      *
 554      * @param cf the type of the Catalog feature
 555      * @return the value of the feature
 556      */
 557     public String get(Feature cf) {
 558         return values[cf.ordinal()];
 559     }
 560 
 561     /**
 562      * Initializes the supported properties
 563      */
 564     private void init() {
 565         values = new String[Feature.values().length];
 566         states = new State[Feature.values().length];
 567         for (Feature cf : Feature.values()) {
 568             setProperty(cf, State.DEFAULT, cf.defaultValue());
 569         }
 570         //read system properties or jaxp.properties
 571         readSystemProperties();
 572     }
 573 
 574     /**
 575      * Sets properties by the Builder.
 576      * @param builder the CatalogFeatures builder
 577      */
 578     private void setProperties(Builder builder) {
 579         builder.values.entrySet().stream().forEach((entry) -> {
 580             setProperty(entry.getKey(), State.APIPROPERTY, entry.getValue());
 581         });
 582     }
 583     /**
 584      * Sets the value of a property, updates only if it shall override.
 585      *
 586      * @param index the index of the property
 587      * @param state the state of the property
 588      * @param value the value of the property
 589      * @throws IllegalArgumentException if the value is invalid
 590      */
 591     private void setProperty(Feature feature, State state, String value) {
 592         int index = feature.ordinal();
 593         if (value != null && value.length() != 0) {
 594             if (state != State.APIPROPERTY) {
 595                 Util.validateFeatureInput(feature, value);
 596             }
 597             if (states[index] == null || state.compareTo(states[index]) >= 0) {
 598                 values[index] = value;
 599                 states[index] = state;
 600             }
 601         }
 602     }
 603 
 604     /**
 605      * Reads from system properties, or those in jaxp.properties
 606      */
 607     private void readSystemProperties() {
 608         for (Feature cf : Feature.values()) {
 609             getSystemProperty(cf, cf.getPropertyName());
 610         }
 611     }
 612 
 613     /**
 614      * Reads from system properties, or those in jaxp.properties
 615      *
 616      * @param cf the type of the property
 617      * @param sysPropertyName the name of system property
 618      */
 619     private boolean getSystemProperty(Feature cf, String sysPropertyName) {
 620         if (cf.hasSystemProperty()) {
 621             String value = SecuritySupport.getSystemProperty(sysPropertyName);
 622             if (value != null && !value.isEmpty()) {
 623                 setProperty(cf, State.SYSTEMPROPERTY, value);
 624                 return true;
 625             }
 626 
 627             value = SecuritySupport.readJAXPProperty(sysPropertyName);
 628             if (value != null && !value.isEmpty()) {
 629                 setProperty(cf, State.JAXPDOTPROPERTIES, value);
 630                 return true;
 631             }
 632         }
 633         return false;
 634     }
 635 
 636     /**
 637      * Returns an instance of the builder for creating the CatalogFeatures object.
 638      *
 639      * @return an instance of the builder
 640      */
 641     public static Builder builder() {
 642         return new CatalogFeatures.Builder();
 643     }
 644 
 645     /**
 646      * The Builder class for building the CatalogFeatures object.
 647      */
 648     public static class Builder {
 649         /**
 650          * Values of the features supported by CatalogFeatures.
 651          */
 652         Map<Feature, String> values = new HashMap<>();
 653 
 654         /**
 655          * Instantiation of Builder is not allowed.
 656          */
 657         private Builder() {}
 658 
 659         /**
 660          * Sets the value to a specified Feature.
 661          * @param feature the Feature to be set
 662          * @param value the value to be set for the Feature
 663          * @return this Builder instance
 664          * @throws IllegalArgumentException if the value is not valid for the
 665          * Feature or has the wrong syntax for the {@code javax.xml.catalog.files}
 666          * property
 667          */
 668         public Builder with(Feature feature, String value) {
 669             Util.validateFeatureInput(feature, value);
 670             values.put(feature, value);
 671             return this;
 672         }
 673 
 674         /**
 675          * Returns a CatalogFeatures object built by this builder.
 676          *
 677          * @return an instance of CatalogFeatures
 678          */
 679         public CatalogFeatures build() {
 680             return new CatalogFeatures(this);
 681         }
 682     }
 683 }