Coverage Report - com.jcabi.xml.XPathContext
 
Classes in this File Line Coverage Branch Coverage Complexity
XPathContext
76%
42/55
40%
20/50
2.455
 
 1  
 /**
 2  
  * Copyright (c) 2012-2017, jcabi.com
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions
 7  
  * are met: 1) Redistributions of source code must retain the above
 8  
  * copyright notice, this list of conditions and the following
 9  
  * disclaimer. 2) Redistributions in binary form must reproduce the above
 10  
  * copyright notice, this list of conditions and the following
 11  
  * disclaimer in the documentation and/or other materials provided
 12  
  * with the distribution. 3) Neither the name of the jcabi.com nor
 13  
  * the names of its contributors may be used to endorse or promote
 14  
  * products derived from this software without specific prior written
 15  
  * permission.
 16  
  *
 17  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 19  
  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 20  
  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 21  
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 22  
  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24  
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 25  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 26  
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 27  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 28  
  * OF THE POSSIBILITY OF SUCH DAMAGE.
 29  
  */
 30  
 package com.jcabi.xml;
 31  
 
 32  
 import com.jcabi.immutable.Array;
 33  
 import com.jcabi.immutable.ArrayMap;
 34  
 import com.jcabi.log.Logger;
 35  
 import java.util.Collections;
 36  
 import java.util.Iterator;
 37  
 import java.util.LinkedList;
 38  
 import java.util.List;
 39  
 import java.util.concurrent.ConcurrentHashMap;
 40  
 import java.util.concurrent.ConcurrentMap;
 41  
 import javax.xml.XMLConstants;
 42  
 import javax.xml.namespace.NamespaceContext;
 43  
 import lombok.EqualsAndHashCode;
 44  
 
 45  
 /**
 46  
  * Convenient internal implementation of {@link NamespaceContext}.
 47  
  *
 48  
  * <p>The class is immutable and thread-safe.
 49  
  *
 50  
  * @author Yegor Bugayenko (yegor@teamed.io)
 51  
  * @version $Id: 0cef13a7e6588d1b413d96f2523d038f4e4254e6 $
 52  
  * @since 0.1
 53  
  */
 54  0
 @EqualsAndHashCode(of = { "map", "contexts" })
 55  
 public final class XPathContext implements NamespaceContext {
 56  
 
 57  
     /**
 58  
      * Map of prefixes and URIs.
 59  
      */
 60  
     private final transient ArrayMap<String, String> map;
 61  
 
 62  
     /**
 63  
      * List of contexts to use.
 64  
      */
 65  
     private final transient Array<NamespaceContext> contexts;
 66  
 
 67  
     /**
 68  
      * Public ctor.
 69  
      *
 70  
      * <p>Since this class is private in the package users won't be able
 71  
      * to see this code and its documentation. That's why all these prefixes
 72  
      * and namespaces should be documented in
 73  
      * {@link XMLDocument#XMLDocument(String)} ctor. When adding/changing this
 74  
      * list - don't forget to document it there.
 75  
      */
 76  
     public XPathContext() {
 77  887
         this(
 78  
             new ArrayMap<String, String>()
 79  
                 .with("xhtml", "http://www.w3.org/1999/xhtml")
 80  
                 .with("xs", "http://www.w3.org/2001/XMLSchema")
 81  
                 .with("xsi", "http://www.w3.org/2001/XMLSchema-instance")
 82  
                 .with("xsl", "http://www.w3.org/1999/XSL/Transform")
 83  
                 .with("svg", "http://www.w3.org/2000/svg"),
 84  
             new Array<NamespaceContext>()
 85  
         );
 86  886
     }
 87  
 
 88  
     /**
 89  
      * Public ctor with custom namespaces.
 90  
      * @param namespaces List of namespaces
 91  
      */
 92  
     public XPathContext(final Object... namespaces) {
 93  1
         this(
 94  
             XPathContext.namespacesAsMap(namespaces),
 95  
             new Array<NamespaceContext>()
 96  
         );
 97  1
     }
 98  
 
 99  
     /**
 100  
      * Public ctor.
 101  
      * @param old Old map of prefixes and namespaces
 102  
      * @param prefix The prefix
 103  
      * @param namespace The namespace
 104  
      */
 105  
     private XPathContext(final ArrayMap<String, String> old,
 106  
         final String prefix, final Object namespace) {
 107  5
         this(
 108  
             old.with(prefix, namespace.toString()),
 109  
             new Array<NamespaceContext>()
 110  
         );
 111  5
     }
 112  
 
 113  
     /**
 114  
      * Private ctor.
 115  
      * @param mapping Mapping to set.
 116  
      * @param ctxs Context to set.
 117  
      */
 118  
     private XPathContext(final ArrayMap<String, String> mapping,
 119  1101
         final Array<NamespaceContext> ctxs) {
 120  1101
         this.map = mapping;
 121  1101
         this.contexts = ctxs;
 122  1101
     }
 123  
 
 124  
     @Override
 125  
     public String toString() {
 126  0
         return this.map.keySet().toString();
 127  
     }
 128  
 
 129  
     @Override
 130  
     public String getNamespaceURI(final String prefix) {
 131  16
         String namespace = this.map.get(prefix);
 132  16
         if (namespace == null) {
 133  3
             for (final NamespaceContext ctx : this.contexts) {
 134  0
                 namespace = ctx.getNamespaceURI(prefix);
 135  0
                 if (namespace != null) {
 136  0
                     break;
 137  
                 }
 138  0
             }
 139  
         }
 140  16
         if (namespace == null) {
 141  3
             if (prefix.equals(XMLConstants.XML_NS_PREFIX)) {
 142  1
                 namespace = XMLConstants.XML_NS_URI;
 143  2
             } else if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
 144  1
                 namespace = XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
 145  
             } else {
 146  1
                 namespace = XMLConstants.NULL_NS_URI;
 147  
             }
 148  
         }
 149  16
         return namespace;
 150  
     }
 151  
 
 152  
     @Override
 153  
     public String getPrefix(final String namespace) {
 154  1
         final Iterator<String> prefixes = this.getPrefixes(namespace);
 155  1
         String prefix = null;
 156  1
         if (prefixes.hasNext()) {
 157  1
             prefix = prefixes.next();
 158  
         }
 159  1
         return prefix;
 160  
     }
 161  
 
 162  
     @Override
 163  
     public Iterator<String> getPrefixes(final String namespace) {
 164  2
         final List<String> prefixes = new LinkedList<String>();
 165  
         for (final ConcurrentMap.Entry<String, String> entry
 166  2
             : this.map.entrySet()) {
 167  9
             if (entry.getValue().equals(namespace)) {
 168  3
                 prefixes.add(entry.getKey());
 169  
             }
 170  9
         }
 171  2
         for (final NamespaceContext ctx : this.contexts) {
 172  0
             final Iterator<?> iterator = ctx.getPrefixes(namespace);
 173  0
             while (iterator.hasNext()) {
 174  0
                 prefixes.add(iterator.next().toString());
 175  
             }
 176  0
         }
 177  2
         if (namespace.equals(XMLConstants.XML_NS_URI)) {
 178  0
             prefixes.add(XMLConstants.XML_NS_PREFIX);
 179  
         }
 180  2
         if (namespace.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
 181  0
             prefixes.add(XMLConstants.XMLNS_ATTRIBUTE);
 182  
         }
 183  2
         return Collections.unmodifiableList(prefixes).iterator();
 184  
     }
 185  
 
 186  
     /**
 187  
      * Add new prefix and namespace.
 188  
      * @param prefix The prefix
 189  
      * @param namespace The namespace
 190  
      * @return New context
 191  
      */
 192  
     public XPathContext add(final String prefix, final Object namespace) {
 193  5
         if (this.map.containsKey(prefix)) {
 194  0
             throw new IllegalArgumentException(
 195  
                 String.format(
 196  
                     "prefix '%s' already registered for namespace '%s'",
 197  
                     prefix,
 198  
                     this.map.get(prefix)
 199  
                 )
 200  
             );
 201  
         }
 202  5
         return new XPathContext(this.map, prefix, namespace);
 203  
     }
 204  
 
 205  
     /**
 206  
      * Add new context.
 207  
      * @param context The context to merge into this one
 208  
      * @return New context
 209  
      */
 210  
     public XPathContext merge(final NamespaceContext context) {
 211  209
         final XPathContext ctx = new XPathContext(
 212  
             this.map, this.contexts.with(context)
 213  
         );
 214  209
         return ctx;
 215  
     }
 216  
 
 217  
     /**
 218  
      * Get namespaces as map.
 219  
      * @param namespaces The namespaces
 220  
      * @return Namespaces as map
 221  
      */
 222  
     private static ArrayMap<String, String> namespacesAsMap(
 223  
         final Object...namespaces) {
 224  1
         final ConcurrentMap<String, String> map =
 225  
             new ConcurrentHashMap<String, String>(namespaces.length);
 226  3
         for (int pos = 0; pos < namespaces.length; ++pos) {
 227  2
             map.put(
 228  
                 Logger.format("ns%d", pos + 1),
 229  
                 namespaces[pos].toString()
 230  
             );
 231  
         }
 232  1
         return new ArrayMap<String, String>(map);
 233  
     }
 234  
 
 235  
 }