Home » xmlbeans-2.5.0-src » org.apache.xmlbeans.impl » xpathgen » [javadoc | source]

    1   package org.apache.xmlbeans.impl.xpathgen;
    2   
    3   import org.apache.xmlbeans.XmlCursor;
    4   import org.apache.xmlbeans.XmlCursor.TokenType;
    5   
    6   import javax.xml.namespace.QName;
    7   import javax.xml.namespace.NamespaceContext;
    8   
    9   /**
   10    * Generates an XPath String that points to a given position in an XML document
   11    */
   12   public class XPathGenerator
   13   {
   14       /**
   15        * Generates an XPath pointing to the position in the document indicated by <code>node</code>.
   16        * <p>If the <code>context</code> parameter is null, the XPath is absolute, otherwise the
   17        * XPath will be relative to the position indicated by <code>context</code>.</p>
   18        * <p>Note: the cursor position for the <code>node</code> parameter is not preserved</p>
   19        * @param node the position in the document that the generated path will point to
   20        * @param context the context node; the generated path will be relative to it if not null and if
   21        * pointing to an element on the path from the document root to <code>node</code>
   22        * @param nsctx a namespace context that will be used to obtain prefixes; a (non-default)
   23        * namespace mapping must be available for all required namespace URIs
   24        * @return the generated path as a <code>String</code>
   25        * @throws XPathGenerationException if the path could not be generated: the cursor is in a bad
   26        * position (like over a comment) or no prefix mapping was found for one of the namespace URIs
   27        */
   28       public static String generateXPath(XmlCursor node, XmlCursor context, NamespaceContext nsctx)
   29           throws XPathGenerationException
   30       {
   31           if (node == null)
   32               throw new IllegalArgumentException("Null node");
   33           if (nsctx == null)
   34               throw new IllegalArgumentException("Null namespace context");
   35           TokenType tt = node.currentTokenType();
   36           if (context != null && node.isAtSamePositionAs(context))
   37               return ".";
   38           switch (tt.intValue())
   39           {
   40               case TokenType.INT_ATTR:
   41                   QName name = node.getName();
   42                   node.toParent();
   43                   String pathToParent = generateInternal(node, context, nsctx);
   44                   return pathToParent + '/' + '@' + qnameToString(name, nsctx);
   45               case TokenType.INT_NAMESPACE:
   46                   name = node.getName();
   47                   node.toParent();
   48                   pathToParent = generateInternal(node, context, nsctx);
   49                   String prefix = name.getLocalPart();
   50                   if (prefix.length() == 0)
   51                       return pathToParent + "/@xmlns";
   52                   else
   53                       return pathToParent + "/@xmlns:" + prefix;
   54               case TokenType.INT_START:
   55               case TokenType.INT_STARTDOC:
   56                   return generateInternal(node, context, nsctx);
   57               case TokenType.INT_TEXT:
   58                   int nrOfTextTokens = countTextTokens(node);
   59                   node.toParent();
   60                   pathToParent = generateInternal(node, context, nsctx);
   61                   if (nrOfTextTokens == 0)
   62                       return pathToParent + "/text()";
   63                   else
   64                       return pathToParent + "/text()[position()=" + nrOfTextTokens + ']';
   65               default:
   66                   throw new XPathGenerationException("Cannot generate XPath for cursor position: " +
   67                       tt.toString());
   68           }
   69       }
   70   
   71       private static String generateInternal(XmlCursor node, XmlCursor context, NamespaceContext nsctx)
   72           throws XPathGenerationException
   73       {
   74           if (node.isStartdoc())
   75               return "";
   76           if (context != null && node.isAtSamePositionAs(context))
   77               return ".";
   78           assert node.isStart();
   79           QName name = node.getName();
   80           XmlCursor d = node.newCursor();
   81           if (!node.toParent())
   82               return "/" + name;
   83           int elemIndex = 0, i = 1;
   84           node.push();
   85           if (!node.toChild(name))
   86               throw new IllegalStateException("Must have at least one child with name: " + name);
   87           do
   88           {
   89               if (node.isAtSamePositionAs(d))
   90                   elemIndex = i;
   91               else
   92                   i++;
   93           } while (node.toNextSibling(name));
   94           node.pop();
   95           d.dispose();
   96           String pathToParent = generateInternal(node, context, nsctx);
   97           return  i == 1 ? pathToParent + '/' + qnameToString(name, nsctx) :
   98               pathToParent + '/' + qnameToString(name, nsctx) + '[' + elemIndex + ']';
   99       }
  100   
  101       private static String qnameToString(QName qname, NamespaceContext ctx)
  102           throws XPathGenerationException
  103       {
  104           String localName = qname.getLocalPart();
  105           String uri = qname.getNamespaceURI();
  106           if (uri.length() == 0)
  107               return localName;
  108           String prefix = qname.getPrefix();
  109           if (prefix != null && prefix.length() > 0)
  110           {
  111               // Try to use the same prefix if it maps to the right URI
  112               String mappedUri = ctx.getNamespaceURI(prefix);
  113               if (uri.equals(mappedUri))
  114                   return prefix + ':' + localName;
  115           }
  116           // The prefix is not specified, or it is not mapped to the right URI
  117           prefix = ctx.getPrefix(uri);
  118           if (prefix == null)
  119               throw new XPathGenerationException("Could not obtain a prefix for URI: " + uri);
  120           if (prefix.length() == 0)
  121               throw new XPathGenerationException("Can not use default prefix in XPath for URI: " + uri);
  122           return prefix + ':' + localName;
  123       }
  124   
  125       /**
  126        * Computes how many text nodes the 
  127        * @param c the position in the document
  128        * @return how many text nodes occur before the position determined by <code>c</code>
  129        */
  130       private static int countTextTokens(XmlCursor c)
  131       {
  132           int k = 0;
  133           int l = 0;
  134           XmlCursor d = c.newCursor();
  135           c.push();
  136           c.toParent();
  137           TokenType tt = c.toFirstContentToken();
  138           while (!tt.isEnd())
  139           {
  140               if (tt.isText())
  141               {
  142                   if (c.comparePosition(d) > 0)
  143                       // We have moved after the initial position
  144                       l++;
  145                   else
  146                       k++;
  147               }
  148               else if (tt.isStart())
  149                   c.toEndToken();
  150               tt = c.toNextToken();
  151           }
  152           c.pop();
  153           return l == 0 ? 0 : k;
  154       }
  155   
  156       public static void main(String[] args) throws org.apache.xmlbeans.XmlException
  157       {
  158           String xml =
  159               "<root>\n" +
  160                   "<ns:a xmlns:ns=\"http://a.com\"><b foo=\"value\">text1<c/>text2<c/>text3<c>text</c>text4</b></ns:a>\n" +
  161                   "</root>";
  162           NamespaceContext ns = new NamespaceContext() {
  163               public String getNamespaceURI(String prefix)
  164               {
  165                   if ("ns".equals(prefix))
  166                       return "http://a.com";
  167                   else
  168                       return null;
  169               }
  170               public String getPrefix(String namespaceUri)
  171               {
  172                   return null;
  173               }
  174               public java.util.Iterator getPrefixes(String namespaceUri)
  175               {
  176                   return null;
  177               }
  178           };
  179           XmlCursor c = org.apache.xmlbeans.XmlObject.Factory.parse(xml).newCursor();
  180           c.toFirstContentToken(); // on <root>
  181           c.toFirstContentToken(); // on <a>
  182           c.toFirstChild();        // on <b>
  183           c.toFirstChild();        // on <c>
  184           c.push(); System.out.println(generateXPath(c, null, ns)); c.pop();
  185           c.toNextSibling();
  186           c.toNextSibling();       // on the last <c>
  187           c.push(); System.out.println(generateXPath(c, null, ns)); c.pop();
  188           XmlCursor d = c.newCursor();
  189           d.toParent();
  190           c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
  191           d.toParent();
  192           c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
  193           c.toFirstContentToken(); // on text content of the last <c>
  194           c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
  195           c.toParent();
  196           c.toPrevToken();         // on text content before the last <c>
  197           c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
  198           c.toParent();            // on <b>
  199           c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
  200           c.toFirstAttribute();    // on the "foo" attribute
  201           c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
  202           c.toParent();
  203           c.toParent();
  204           c.toNextToken();         // on the "xmlns:ns" attribute
  205           c.push(); System.out.println(generateXPath(c, d, ns)); c.pop();
  206           c.push(); System.out.println(generateXPath(c, null, ns)); c.pop();
  207       }
  208   }

Home » xmlbeans-2.5.0-src » org.apache.xmlbeans.impl » xpathgen » [javadoc | source]