IDEA-53813

This commit is contained in:
sweinreuter
2010-04-15 19:39:39 +02:00
parent 5694620912
commit 8e5ca99e69
2 changed files with 157 additions and 67 deletions
@@ -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<String, String> usedPrefixes = new HashMap<String, String>();
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;
}
}
}
@@ -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<Namespace> {
public String prefix;
@@ -65,6 +63,14 @@ public final class Namespace implements Cloneable, Copyable<Namespace> {
return clone();
}
public static Collection<Namespace> fromMap(Map<String, String> namespaces) {
final List<Namespace> list = new ArrayList<Namespace>(namespaces.size());
for (Map.Entry<String, String> e : namespaces.entrySet()) {
list.add(new Namespace(e.getKey(), e.getValue()));
}
return list;
}
public static Map<String, String> makeMap(Collection<Namespace> namespaces) {
final Map<String, String> _ns = new HashMap<String, String>();
for (Namespace namespace : namespaces) {