From 8e5ca99e690e088f927a1b361bf980cc2cbb3238 Mon Sep 17 00:00:00 2001 From: sweinreuter Date: Thu, 15 Apr 2010 19:39:39 +0200 Subject: [PATCH] IDEA-53813 --- .../xpathView/XPathExpressionGenerator.java | 212 ++++++++++++------ .../plugins/xpathView/util/Namespace.java | 12 +- 2 files changed, 157 insertions(+), 67 deletions(-) diff --git a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/XPathExpressionGenerator.java b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/XPathExpressionGenerator.java index d58f635a5da2..38bf9cdc65c6 100644 --- a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/XPathExpressionGenerator.java +++ b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/XPathExpressionGenerator.java @@ -15,22 +15,23 @@ */ package org.intellij.plugins.xpathView; -import org.intellij.plugins.xpathView.support.XPathSupport; -import org.intellij.plugins.xpathView.support.jaxen.PsiDocumentNavigator; -import org.intellij.plugins.xpathView.util.MyPsiUtil; - import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiElement; import com.intellij.psi.XmlElementVisitor; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.*; import com.intellij.xml.XmlAttributeDescriptor; -import org.jetbrains.annotations.Nullable; - +import org.intellij.plugins.xpathView.support.XPathSupport; +import org.intellij.plugins.xpathView.support.jaxen.PsiDocumentNavigator; +import org.intellij.plugins.xpathView.util.MyPsiUtil; +import org.intellij.plugins.xpathView.util.Namespace; import org.jaxen.JaxenException; import org.jaxen.XPath; +import org.jetbrains.annotations.Nullable; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class XPathExpressionGenerator { private static final XPathSupport xpathSupport = XPathSupport.getInstance(); @@ -44,59 +45,6 @@ public class XPathExpressionGenerator { return visitor.getUniquePath(); } - static String makeUnique(String uniquePath, XmlElement what) { - final XmlFile file = (XmlFile)what.getContainingFile(); - assert file != null; - try { - final XPath xPath = xpathSupport.createXPath(file, uniquePath); - final Object o = xPath.evaluate(file.getDocument()); - if (o instanceof List) { - //noinspection RawUseOfParameterizedType - final List list = (List)o; - if (list.size() > 1) { - if (what instanceof XmlTag) { - final XmlTag tag = (XmlTag)what; - final XmlAttribute[] attributes = tag.getAttributes(); - if (attributes.length > 0) { - for (XmlAttribute attribute : attributes) { - final String name = attribute.getName(); - final XmlAttributeDescriptor descriptor = attribute.getDescriptor(); - if ((attribute.getValue() != null && - (descriptor != null && descriptor.hasIdType()) || - name.equalsIgnoreCase("id") || - name.equalsIgnoreCase("name"))) - { - final StringBuilder buffer = new StringBuilder(uniquePath); - buffer.append("[@"); - buffer.append(name); - buffer.append("='"); - buffer.append(attribute.getValue()); - buffer.append("']"); - return buffer.toString(); - } - } - } - } - - int i = 1; - for (Object o1 : list) { - if (o1 == what) { - return uniquePath + "[" + i + "]"; - } else { - i++; - } - } - assert false : "Expression " + uniquePath + " didn't find input element " + what; - } - } else { - assert false : "Unknown return value: " + o; - } - } catch (JaxenException e) { - Logger.getInstance("XPathExpressionGenerator").error(e); - } - return uniquePath; - } - public static String getPath(XmlElement element, XmlTag context) { final PathVisitor visitor = new PathVisitor(context); element.accept(visitor); @@ -128,6 +76,7 @@ public class XPathExpressionGenerator { private static class PathVisitor extends XmlElementVisitor { private final XmlTag context; + private final Map usedPrefixes = new HashMap(); private String uniquePath; private String path; @@ -136,7 +85,7 @@ public class XPathExpressionGenerator { } @Nullable - private static String getXPathNameStep(XmlTag tag) { + private String getXPathNameStep(XmlTag tag) { String uri = tag.getNamespace(); if ((uri.length() == 0)) { @@ -146,14 +95,93 @@ public class XPathExpressionGenerator { if (MyPsiUtil.isInDeclaredNamespace(tag, uri, tag.getNamespacePrefix())) { String prefix = tag.getNamespacePrefix(); - // TODO: could be even more strict by checking namespace-uri() equivalence here but would look ugly if (prefix.length() == 0) { - return "*[name()='" + tag.getName() + "']"; + final String guessedPrefix = guessPrefix(tag); + return guessedPrefix != null ? guessedPrefix + ":" + tag.getLocalName() : "*[name()='" + tag.getName() + "']"; } } return tag.getName(); } + @Nullable + private String guessPrefix(XmlTag tag) { + final String prefix = usedPrefixes.get(tag.getNamespace()); + if (prefix != null) { + return prefix; + } + return tryUseUri(tag); + } + + @Nullable + private String tryUseUri(XmlTag context) { + String segment = chooseSegment(context.getNamespace()); + if (segment == null) { + return null; + } + + if (segment.length() <= 3 && tryUsePrefix(segment, context)) { + return segment; + } + + for (int i = 1; i <= segment.length(); i++) { + String prefix = segment.substring(0, i); + if (tryUsePrefix(prefix, context)) return prefix; + } + return null; + } + + private boolean tryUsePrefix(String prefix, XmlTag context) { + if (!prefixOk(prefix, context)) return false; + usePrefix(prefix, context.getNamespace()); + return true; + } + + private boolean prefixOk(String prefix, XmlTag context) { + final String namespace = context.getNamespace(); + if (!usedPrefixes.containsKey(prefix)) { + final String ns = context.getNamespaceByPrefix(prefix); + if (ns.length() == 0 || ns.equals(namespace)) { + return true; + } + } + return namespace.equals(usedPrefixes.get(prefix)); + } + + private void usePrefix(String prefix, String namespace) { + usedPrefixes.put(prefix, namespace); + } + + static private String chooseSegment(String ns) { + int off = ns.indexOf('#'); + if (off >= 0) { + String segment = ns.substring(off + 1).toLowerCase(); + if (isValidPrefix(segment)) return segment; + } else { + off = ns.length(); + } + for (; ;) { + int i = ns.lastIndexOf('/', off - 1); + if (i < 0 || (i > 0 && ns.charAt(i - 1) == '/')) break; + String segment = ns.substring(i + 1, off).toLowerCase(); + if (segmentOk(segment)) return segment; + off = i; + } + off = ns.indexOf(':'); + if (off >= 0) { + String segment = ns.substring(off + 1).toLowerCase(); + if (segmentOk(segment)) return segment; + } + return null; + } + + private static boolean isValidPrefix(String segment) { + return segment.matches("\\p{Alpha}\\p{Alnum}*"); + } + + private static boolean segmentOk(String segment) { + return isValidPrefix(segment) && !segment.equals("ns") && !segment.equals("namespace"); + } + @Override public void visitElement(PsiElement element) { if (element instanceof XmlProcessingInstruction) { @@ -266,12 +294,14 @@ public class XPathExpressionGenerator { uniquePath = getUniquePath(comment); path = getPath(comment); } + public String getPath(XmlComment comment) { XmlTag parent = PsiTreeUtil.getParentOfType(comment, XmlTag.class); return ((parent != null) && (parent != context)) ? (getPath(parent) + "/comment()") - : "comment()"; + : "comment()"; } + public String getUniquePath(XmlComment comment) { XmlTag parent = PsiTreeUtil.getParentOfType(comment, XmlTag.class); @@ -284,12 +314,14 @@ public class XPathExpressionGenerator { uniquePath = getUniquePath(text); path = getPath(text); } + public String getPath(XmlText text) { XmlTag parent = PsiTreeUtil.getParentOfType(text, XmlTag.class); return ((parent != null) && (parent != context)) ? (getPath(parent) + "/text()") - : "text()"; + : "text()"; } + public String getUniquePath(XmlText text) { XmlTag parent = PsiTreeUtil.getParentOfType(text, XmlTag.class); @@ -325,5 +357,57 @@ public class XPathExpressionGenerator { public String getPath() { return path; } + + String makeUnique(String uniquePath, XmlElement what) { + final XmlFile file = (XmlFile)what.getContainingFile(); + assert file != null; + try { + final XPath xPath = xpathSupport.createXPath(file, uniquePath, Namespace.fromMap(usedPrefixes)); + final Object o = xPath.evaluate(file.getDocument()); + if (o instanceof List) { + //noinspection RawUseOfParameterizedType + final List list = (List)o; + if (list.size() > 1) { + if (what instanceof XmlTag) { + final XmlTag tag = (XmlTag)what; + final XmlAttribute[] attributes = tag.getAttributes(); + if (attributes.length > 0) { + for (XmlAttribute attribute : attributes) { + final String name = attribute.getName(); + final XmlAttributeDescriptor descriptor = attribute.getDescriptor(); + if ((attribute.getValue() != null && + (descriptor != null && descriptor.hasIdType()) || + name.equalsIgnoreCase("id") || + name.equalsIgnoreCase("name"))) { + final StringBuilder buffer = new StringBuilder(uniquePath); + buffer.append("[@"); + buffer.append(name); + buffer.append("='"); + buffer.append(attribute.getValue()); + buffer.append("']"); + return buffer.toString(); + } + } + } + } + + int i = 1; + for (Object o1 : list) { + if (o1 == what) { + return uniquePath + "[" + i + "]"; + } else { + i++; + } + } + assert false : "Expression " + uniquePath + " didn't find input element " + what; + } + } else { + assert false : "Unknown return value: " + o; + } + } catch (JaxenException e) { + Logger.getInstance("XPathExpressionGenerator").error(e); + } + return uniquePath; + } } } diff --git a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/util/Namespace.java b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/util/Namespace.java index 47f7a1677493..3f70fd98a862 100644 --- a/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/util/Namespace.java +++ b/plugins/xpath/xpath-view/src/org/intellij/plugins/xpathView/util/Namespace.java @@ -15,9 +15,7 @@ */ package org.intellij.plugins.xpathView.util; -import java.util.Map; -import java.util.Collection; -import java.util.HashMap; +import java.util.*; public final class Namespace implements Cloneable, Copyable { public String prefix; @@ -65,6 +63,14 @@ public final class Namespace implements Cloneable, Copyable { return clone(); } + public static Collection fromMap(Map namespaces) { + final List list = new ArrayList(namespaces.size()); + for (Map.Entry e : namespaces.entrySet()) { + list.add(new Namespace(e.getKey(), e.getValue())); + } + return list; + } + public static Map makeMap(Collection namespaces) { final Map _ns = new HashMap(); for (Namespace namespace : namespaces) {