View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  
18  package org.apache.commons.text.lookup;
19  
20  import java.io.InputStream;
21  import java.nio.file.Files;
22  import java.nio.file.Path;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.Map.Entry;
26  import java.util.Objects;
27  
28  import javax.xml.XMLConstants;
29  import javax.xml.xpath.XPathFactory;
30  
31  import org.apache.commons.lang3.StringUtils;
32  import org.xml.sax.InputSource;
33  
34  /**
35   * Looks up keys from an XML document.
36   * <p>
37   * Looks up the value for a given key in the format "Document:XPath".
38   * </p>
39   * <p>
40   * For example: "com/domain/document.xml:/path/to/node".
41   * </p>
42   *
43   * @since 1.5
44   */
45  final class XmlStringLookup extends AbstractPathFencedLookup {
46  
47      /**
48       * Defines default XPath factory features.
49       */
50      static final Map<String, Boolean> DEFAULT_FEATURES;
51  
52      static {
53          DEFAULT_FEATURES = new HashMap<>(1);
54          DEFAULT_FEATURES.put(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
55      }
56  
57      /**
58       * Defines the singleton for this class.
59       */
60      static final XmlStringLookup INSTANCE = new XmlStringLookup(DEFAULT_FEATURES, (Path[]) null);
61  
62      /**
63       * Defines XPath factory features.
64       */
65      private final Map<String, Boolean> xPathFactoryFeatures;
66  
67      /**
68       * No need to build instances for now.
69       *
70       * @param xPathFactoryFeatures XPathFactory features to set.
71       * @see XPathFactory#setFeature(String, boolean)
72       */
73      XmlStringLookup(final Map<String, Boolean> xPathFactoryFeatures, final Path... fences) {
74          super(fences);
75          this.xPathFactoryFeatures = Objects.requireNonNull(xPathFactoryFeatures, "xPathFfactoryFeatures");
76      }
77  
78      /**
79       * Looks up the value for the key in the format "DocumentPath:XPath".
80       * <p>
81       * For example: "com/domain/document.xml:/path/to/node".
82       * </p>
83       *
84       * @param key the key to be looked up, may be null
85       * @return The value associated with the key.
86       */
87      @Override
88      public String lookup(final String key) {
89          if (key == null) {
90              return null;
91          }
92          final String[] keys = key.split(SPLIT_STR);
93          final int keyLen = keys.length;
94          if (keyLen != 2) {
95              throw IllegalArgumentExceptions.format("Bad XML key format [%s]; expected format is DocumentPath:XPath.",
96                      key);
97          }
98          final String documentPath = keys[0];
99          final String xpath = StringUtils.substringAfter(key, SPLIT_CH);
100         try (InputStream inputStream = Files.newInputStream(getPath(documentPath))) {
101             final XPathFactory factory = XPathFactory.newInstance();
102             for (final Entry<String, Boolean> p : xPathFactoryFeatures.entrySet()) {
103                 factory.setFeature(p.getKey(), p.getValue());
104             }
105             return factory.newXPath().evaluate(xpath, new InputSource(inputStream));
106         } catch (final Exception e) {
107             throw IllegalArgumentExceptions.format(e, "Error looking up XML document [%s] and XPath [%s].",
108                     documentPath, xpath);
109         }
110     }
111 
112 }