1/*
2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package com.sun.org.apache.xerces.internal.util;
22
23import com.sun.org.apache.xerces.internal.impl.Constants;
24import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
25import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
26import java.util.Arrays;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.Map;
30import java.util.Set;
31
32/**
33 * This class implements the basic operations for managing parser
34 * configuration features and properties. This utility class can
35 * be used as a base class for parser configurations or separately
36 * to encapsulate a number of parser settings as a component
37 * manager.
38 * <p>
39 * This class can be constructed with a "parent" settings object
40 * (in the form of an <code>XMLComponentManager</code>) that allows
41 * parser configuration settings to be "chained" together.
42 *
43 * @author Andy Clark, IBM
44 *
45 */
46public class ParserConfigurationSettings
47    implements XMLComponentManager {
48
49        protected static final String PARSER_SETTINGS =
50                        Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
51
52    //
53    // Data
54    //
55
56    // data
57
58    /** Recognized properties. */
59    protected Set<String> fRecognizedProperties;
60
61    /** Properties. */
62    protected Map<String, Object> fProperties;
63
64    /** Recognized features. */
65    protected Set<String> fRecognizedFeatures;
66
67    /** Features. */
68    protected Map<String, Boolean> fFeatures;
69
70    /** Parent parser configuration settings. */
71    protected XMLComponentManager fParentSettings;
72
73    //
74    // Constructors
75    //
76
77    /** Default Constructor. */
78    public ParserConfigurationSettings() {
79        this(null);
80    } // <init>()
81
82    /**
83     * Constructs a parser configuration settings object with a
84     * parent settings object.
85     */
86    public ParserConfigurationSettings(XMLComponentManager parent) {
87
88        // create storage for recognized features and properties
89        fRecognizedFeatures = new HashSet<>();
90        fRecognizedProperties = new HashSet<>();
91
92        // create table for features and properties
93        fFeatures = new HashMap<>();
94        fProperties = new HashMap<>();
95
96        // save parent
97        fParentSettings = parent;
98
99    } // <init>(XMLComponentManager)
100
101    //
102    // XMLParserConfiguration methods
103    //
104
105    /**
106     * Allows a parser to add parser specific features to be recognized
107     * and managed by the parser configuration.
108     *
109     * @param featureIds An array of the additional feature identifiers
110     *                   to be recognized.
111     */
112    public void addRecognizedFeatures(String[] featureIds) {
113
114        // add recognized features
115        int featureIdsCount = featureIds != null ? featureIds.length : 0;
116        for (int i = 0; i < featureIdsCount; i++) {
117            String featureId = featureIds[i];
118            if (!fRecognizedFeatures.contains(featureId)) {
119                fRecognizedFeatures.add(featureId);
120            }
121        }
122
123    } // addRecognizedFeatures(String[])
124
125    /**
126     * Set the state of a feature.
127     *
128     * Set the state of any feature in a SAX2 parser.  The parser
129     * might not recognize the feature, and if it does recognize
130     * it, it might not be able to fulfill the request.
131     *
132     * @param featureId The unique identifier (URI) of the feature.
133     * @param state The requested state of the feature (true or false).
134     *
135     * @exception com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException If the
136     *            requested feature is not known.
137     */
138    public void setFeature(String featureId, boolean state)
139        throws XMLConfigurationException {
140
141        // check and store
142        FeatureState checkState = checkFeature(featureId);
143        if (checkState.isExceptional()) {
144            throw new XMLConfigurationException(checkState.status, featureId);
145        }
146
147        fFeatures.put(featureId, state);
148    } // setFeature(String,boolean)
149
150    /**
151     * Allows a parser to add parser specific properties to be recognized
152     * and managed by the parser configuration.
153     *
154     * @param propertyIds An array of the additional property identifiers
155     *                    to be recognized.
156     */
157    public void addRecognizedProperties(String[] propertyIds) {
158        fRecognizedProperties.addAll(Arrays.asList(propertyIds));
159    } // addRecognizedProperties(String[])
160
161    /**
162     * setProperty
163     *
164     * @param propertyId
165     * @param value
166     * @exception com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException If the
167     *            requested feature is not known.
168     */
169    public void setProperty(String propertyId, Object value)
170        throws XMLConfigurationException {
171
172        // check and store
173        PropertyState checkState = checkProperty(propertyId);
174        if (checkState.isExceptional()) {
175            throw new XMLConfigurationException(checkState.status, propertyId);
176        }
177        fProperties.put(propertyId, value);
178
179    } // setProperty(String,Object)
180
181    //
182    // XMLComponentManager methods
183    //
184
185    /**
186     * Returns the state of a feature.
187     *
188     * @param featureId The feature identifier.
189                 * @return true if the feature is supported
190     *
191     * @throws XMLConfigurationException Thrown for configuration error.
192     *                                   In general, components should
193     *                                   only throw this exception if
194     *                                   it is <strong>really</strong>
195     *                                   a critical error.
196     */
197    @Override
198    public final boolean getFeature(String featureId)
199        throws XMLConfigurationException {
200
201        FeatureState state = getFeatureState(featureId);
202        if (state.isExceptional()) {
203            throw new XMLConfigurationException(state.status, featureId);
204        }
205        return state.state;
206    } // getFeature(String):boolean
207
208    @Override
209    public final boolean getFeature(String featureId, boolean defaultValue) {
210        FeatureState state = getFeatureState(featureId);
211        if (state.isExceptional()) {
212            return defaultValue;
213        }
214        return state.state;
215    }
216
217    @Override
218    public FeatureState getFeatureState(String featureId) {
219        Boolean state = fFeatures.get(featureId);
220
221        if (state == null) {
222            FeatureState checkState = checkFeature(featureId);
223            if (checkState.isExceptional()) {
224                return checkState;
225            }
226            return FeatureState.is(false);
227        }
228        return FeatureState.is(state);
229    }
230
231    /**
232     * Returns the value of a property.
233     *
234     * @param propertyId The property identifier.
235                 * @return the value of the property
236     *
237     * @throws XMLConfigurationException Thrown for configuration error.
238     *                                   In general, components should
239     *                                   only throw this exception if
240     *                                   it is <strong>really</strong>
241     *                                   a critical error.
242     */
243    @Override
244    public final Object getProperty(String propertyId)
245        throws XMLConfigurationException {
246
247        PropertyState state = getPropertyState(propertyId);
248        if (state.isExceptional()) {
249            throw new XMLConfigurationException(state.status, propertyId);
250        }
251
252        return state.state;
253    } // getProperty(String):Object
254
255    @Override
256    public final Object getProperty(String propertyId, Object defaultValue) {
257        PropertyState state = getPropertyState(propertyId);
258        if (state.isExceptional()) {
259            return defaultValue;
260        }
261
262        return state.state;
263    }
264
265    @Override
266    public PropertyState getPropertyState(String propertyId) {
267        Object propertyValue = fProperties.get(propertyId);
268
269        if (propertyValue == null) {
270            PropertyState state = checkProperty(propertyId);
271            if (state.isExceptional()) {
272                return state;
273            }
274        }
275
276        return PropertyState.is(propertyValue);
277    }
278
279    //
280    // Protected methods
281    //
282
283    /**
284     * Check a feature. If feature is known and supported, this method simply
285     * returns. Otherwise, the appropriate exception is thrown.
286     *
287     * @param featureId The unique identifier (URI) of the feature.
288     *
289     * @exception com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException If the
290     *            requested feature is not known.
291     */
292    protected FeatureState checkFeature(String featureId)
293        throws XMLConfigurationException {
294
295        // check feature
296        if (!fRecognizedFeatures.contains(featureId)) {
297            if (fParentSettings != null) {
298                return fParentSettings.getFeatureState(featureId);
299            }
300            else {
301                return FeatureState.NOT_RECOGNIZED;
302            }
303        }
304
305        // TODO: reasonable default?
306        return FeatureState.RECOGNIZED;
307    } // checkFeature(String)
308
309    /**
310     * Check a property. If the property is known and supported, this method
311     * simply returns. Otherwise, the appropriate exception is thrown.
312     *
313     * @param propertyId The unique identifier (URI) of the property
314     *                   being set.
315     * @return the PropertyState
316     * @exception com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException If the
317     *            requested feature is not known.
318     */
319    protected PropertyState checkProperty(String propertyId)
320        throws XMLConfigurationException {
321
322        // check property
323        if (!fRecognizedProperties.contains(propertyId)) {
324            if (fParentSettings != null) {
325                PropertyState state = fParentSettings.getPropertyState(propertyId);
326                if (state.isExceptional()) {
327                    return state;
328                }
329            }
330            else {
331                return PropertyState.NOT_RECOGNIZED;
332            }
333        }
334        return PropertyState.RECOGNIZED;
335    } // checkProperty(String)
336
337} // class ParserConfigurationSettings
338