blob: 9de6d6e7e1d652b55deab8fef0d222758d6016bb [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.xml.util;
import com.intellij.codeInsight.completion.CompletionUtilCore;
import com.intellij.codeInsight.daemon.Validator;
import com.intellij.codeInsight.daemon.XmlErrorMessages;
import com.intellij.javaee.ExternalResourceManager;
import com.intellij.javaee.ExternalResourceManagerEx;
import com.intellij.javaee.UriUtil;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.lang.html.HTMLLanguage;
import com.intellij.lang.xhtml.XHTMLLanguage;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.patterns.StandardPatterns;
import com.intellij.patterns.StringPattern;
import com.intellij.patterns.XmlPatterns;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.XmlTagFilter;
import com.intellij.psi.filters.position.FilterPattern;
import com.intellij.psi.impl.source.html.HtmlDocumentImpl;
import com.intellij.psi.impl.source.tree.CompositeElement;
import com.intellij.psi.impl.source.tree.LeafElement;
import com.intellij.psi.impl.source.xml.XmlEntityCache;
import com.intellij.psi.impl.source.xml.XmlEntityRefImpl;
import com.intellij.psi.scope.processor.FilterElementProcessor;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.NullableFunction;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.XmlCharsetDetector;
import com.intellij.xml.*;
import com.intellij.xml.impl.schema.ComplexTypeDescriptor;
import com.intellij.xml.impl.schema.TypeDescriptor;
import com.intellij.xml.impl.schema.XmlElementDescriptorImpl;
import com.intellij.xml.impl.schema.XmlNSDescriptorImpl;
import com.intellij.xml.index.IndexedRelevantResource;
import com.intellij.xml.index.XmlNamespaceIndex;
import com.intellij.xml.index.XsdNamespaceBuilder;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.net.URL;
import java.util.*;
/**
* @author Mike
*/
public class XmlUtil {
@NonNls public static final String TAGLIB_1_2_URI = "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd";
@NonNls public static final String XML_SCHEMA_URI = "http://www.w3.org/2001/XMLSchema";
@NonNls public static final String XML_SCHEMA_URI2 = "http://www.w3.org/1999/XMLSchema";
@NonNls public static final String XML_SCHEMA_URI3 = "http://www.w3.org/2000/10/XMLSchema";
public static final String[] SCHEMA_URIS = {XML_SCHEMA_URI, XML_SCHEMA_URI2, XML_SCHEMA_URI3};
@NonNls public static final String XML_SCHEMA_INSTANCE_URI = "http://www.w3.org/2001/XMLSchema-instance";
@NonNls public static final String XSLT_URI = "http://www.w3.org/1999/XSL/Transform";
@NonNls public static final String XINCLUDE_URI = XmlPsiUtil.XINCLUDE_URI;
@NonNls public static final String ANT_URI = "http://ant.apache.org/schema.xsd";
@NonNls public static final String XHTML_URI = "http://www.w3.org/1999/xhtml";
@NonNls public static final String HTML_URI = "http://www.w3.org/1999/html";
@NonNls public static final String EMPTY_URI = "";
@NonNls public static final Key<String> TEST_PATH = Key.create("TEST PATH");
@NonNls public static final String JSP_URI = "http://java.sun.com/JSP/Page";
@NonNls public static final String ANY_URI = "http://www.intellij.net/ns/any";
@NonNls public static final String JSTL_CORE_URI = "http://java.sun.com/jsp/jstl/core";
@NonNls public static final String JSTL_CORE_URI2 = "http://java.sun.com/jstl/core";
@NonNls public static final String JSTL_CORE_URI3 = "http://java.sun.com/jstl/core_rt";
@NonNls public static final String JSTL_CORE_URI_JAVAEE_7 = "http://xmlns.jcp.org/jsp/jstl/core";
@NonNls public static final String[] JSTL_CORE_URIS = {JSTL_CORE_URI, JSTL_CORE_URI2, JSTL_CORE_URI3, JSTL_CORE_URI_JAVAEE_7};
@NonNls public static final String JSF_HTML_URI = "http://java.sun.com/jsf/html";
@NonNls public static final String JSF_HTML_URI_JAVAEE_7 = "http://xmlns.jcp.org/jsf/html";
@NonNls public static final String[] JSF_HTML_URIS = {JSF_HTML_URI, JSF_HTML_URI_JAVAEE_7};
@NonNls public static final String JSF_CORE_URI = "http://java.sun.com/jsf/core";
@NonNls public static final String JSF_CORE_URI_JAVAEE_7 = "http://xmlns.jcp.org/jsf/core";
@NonNls public static final String[] JSF_CORE_URIS = {JSF_CORE_URI, JSF_CORE_URI_JAVAEE_7};
@NonNls public static final String JSF_PASS_THROUGH_ATTR_URI_JAVAEE7 = "http://xmlns.jcp.org/jsf";
@NonNls public static final String JSF_PASSTHROUGH_URI = "http://xmlns.jcp.org/jsf/passthrough";
@NonNls public static final String JSTL_FORMAT_URI = "http://java.sun.com/jsp/jstl/fmt";
@NonNls public static final String JSTL_FORMAT_URI2 = "http://java.sun.com/jstl/fmt";
@NonNls public static final String SPRING_URI = "http://www.springframework.org/tags";
@NonNls public static final String SPRING_FORMS_URI = "http://www.springframework.org/tags/form";
@NonNls public static final String STRUTS_BEAN_URI = "http://struts.apache.org/tags-bean";
@NonNls public static final String STRUTS_BEAN_URI2 = "http://jakarta.apache.org/struts/tags-bean";
@NonNls public static final String APACHE_I18N_URI = "http://jakarta.apache.org/taglibs/i18n-1.0";
@NonNls public static final String STRUTS_LOGIC_URI = "http://struts.apache.org/tags-logic";
@NonNls public static final String STRUTS_HTML_URI = "http://struts.apache.org/tags-html";
@NonNls public static final String STRUTS_HTML_URI2 = "http://jakarta.apache.org/struts/tags-html";
@NonNls public static final String APACHE_TRINIDAD_URI = "http://myfaces.apache.org/trinidad";
@NonNls public static final String APACHE_TRINIDAD_HTML_URI = "http://myfaces.apache.org/trinidad/html";
@NonNls public static final String XSD_SIMPLE_CONTENT_TAG = "simpleContent";
@NonNls public static final String NO_NAMESPACE_SCHEMA_LOCATION_ATT = "noNamespaceSchemaLocation";
@NonNls public static final String SCHEMA_LOCATION_ATT = "schemaLocation";
@NonNls public static final String[] WEB_XML_URIS =
{"http://java.sun.com/xml/ns/j2ee", "http://java.sun.com/xml/ns/javaee", "http://xmlns.jcp.org/xml/ns/javaee", "http://java.sun.com/dtd/web-app_2_3.dtd",
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"};
@NonNls public static final String FACELETS_URI = "http://java.sun.com/jsf/facelets";
@NonNls public static final String FACELETS_URI_JAVAEE_7 = "http://xmlns.jcp.org/jsf/facelets";
@NonNls public static final String[] FACELETS_URIS = {FACELETS_URI, FACELETS_URI_JAVAEE_7};
@NonNls public static final String JSTL_FUNCTIONS_URI = "http://java.sun.com/jsp/jstl/functions";
@NonNls public static final String JSTL_FUNCTIONS_URI2 = "http://java.sun.com/jstl/functions";
@NonNls public static final String JSTL_FUNCTIONS_JAVAEE_7 = "http://xmlns.jcp.org/jsp/jstl/functions";
@NonNls public static final String[] JSTL_FUNCTIONS_URIS = {JSTL_FUNCTIONS_URI, JSTL_FUNCTIONS_URI2};
@NonNls public static final String JSTL_FN_FACELET_URI = "com.sun.facelets.tag.jstl.fn.JstlFnLibrary";
@NonNls public static final String JSTL_CORE_FACELET_URI = "com.sun.facelets.tag.jstl.core.JstlCoreLibrary";
@NonNls public static final String TARGET_NAMESPACE_ATTR_NAME = "targetNamespace";
@NonNls public static final String XML_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace";
public static final List<String> ourSchemaUrisList = Arrays.asList(SCHEMA_URIS);
public static final Key<Boolean> ANT_FILE_SIGN = new Key<Boolean>("FORCED ANT FILE");
@NonNls public static final String TAG_DIR_NS_PREFIX = "urn:jsptagdir:";
@NonNls public static final String VALUE_ATTR_NAME = "value";
@NonNls public static final String ENUMERATION_TAG_NAME = "enumeration";
@NonNls public static final String HTML4_LOOSE_URI = "http://www.w3.org/TR/html4/loose.dtd";
@NonNls public static final String WSDL_SCHEMA_URI = "http://schemas.xmlsoap.org/wsdl/";
public static final String XHTML4_SCHEMA_LOCATION;
public final static ThreadLocal<Boolean> BUILDING_DOM_STUBS = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return Boolean.FALSE;
}
};
private static final Logger LOG = Logger.getInstance("#com.intellij.xml.util.XmlUtil");
@NonNls private static final String JSTL_FORMAT_URI3 = "http://java.sun.com/jstl/fmt_rt";
@NonNls public static final String[] JSTL_FORMAT_URIS = {JSTL_FORMAT_URI, JSTL_FORMAT_URI2, JSTL_FORMAT_URI3};
@NonNls private static final String FILE = "file:";
@NonNls private static final String CLASSPATH = "classpath:/";
@NonNls private static final String URN = "urn:";
private final static Set<String> doNotVisitTags = new HashSet<String>(Arrays.asList("annotation", "element", "attribute"));
private XmlUtil() {
}
static {
final URL xhtml4SchemaLocationUrl = XmlUtil.class.getResource(ExternalResourceManagerEx.STANDARD_SCHEMAS + "xhtml1-transitional.xsd");
XHTML4_SCHEMA_LOCATION = VfsUtilCore.urlToPath(VfsUtilCore.toIdeaUrl(FileUtil.unquote(xhtml4SchemaLocationUrl.toExternalForm()), false));
}
public static String getSchemaLocation(XmlTag tag, final String namespace) {
while (tag != null) {
String schemaLocation = tag.getAttributeValue(SCHEMA_LOCATION_ATT, XML_SCHEMA_INSTANCE_URI);
if (schemaLocation != null) {
StringTokenizer tokenizer = new StringTokenizer(schemaLocation);
int i = 0;
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (i % 2 == 0 && namespace.equals(token) && tokenizer.hasMoreTokens()) {
return tokenizer.nextToken();
}
i++;
}
}
tag = tag.getParentTag();
}
return namespace;
}
@Nullable
public static String findNamespacePrefixByURI(XmlFile file, @NotNull @NonNls String uri) {
final XmlTag tag = file.getRootTag();
if (tag == null) return null;
for (XmlAttribute attribute : tag.getAttributes()) {
if (attribute.getName().startsWith("xmlns:") && uri.equals(attribute.getValue())) {
return attribute.getName().substring("xmlns:".length());
}
if ("xmlns".equals(attribute.getName()) && uri.equals(attribute.getValue())) return "";
}
return null;
}
public static String[] findNamespacesByURI(XmlFile file, String uri) {
if (file == null) return ArrayUtil.EMPTY_STRING_ARRAY;
final XmlDocument document = file.getDocument();
if (document == null) return ArrayUtil.EMPTY_STRING_ARRAY;
final XmlTag tag = document.getRootTag();
if (tag == null) return ArrayUtil.EMPTY_STRING_ARRAY;
XmlAttribute[] attributes = tag.getAttributes();
List<String> result = new ArrayList<String>();
for (XmlAttribute attribute : attributes) {
if (attribute.getName().startsWith("xmlns:") && attribute.getValue().equals(uri)) {
result.add(attribute.getName().substring("xmlns:".length()));
}
if ("xmlns".equals(attribute.getName()) && attribute.getValue().equals(uri)) result.add("");
}
return ArrayUtil.toStringArray(result);
}
@Nullable
public static String getXsiNamespace(XmlFile file) {
return findNamespacePrefixByURI(file, XML_SCHEMA_INSTANCE_URI);
}
@Nullable
public static XmlFile findNamespace(@NotNull PsiFile base, @NotNull String nsLocation) {
final String location = ExternalResourceManager.getInstance().getResourceLocation(nsLocation, base.getProject());
if (!location.equals(nsLocation)) { // is mapped
return findXmlFile(base, location);
}
final XmlFile xmlFile = XmlSchemaProvider.findSchema(location, base);
return xmlFile == null ? findXmlFile(base, location) : xmlFile;
}
@Nullable
public static XmlFile findNamespaceByLocation(@NotNull PsiFile base, @NotNull String nsLocation) {
final String location = ExternalResourceManager.getInstance().getResourceLocation(nsLocation, base.getProject());
return findXmlFile(base, location);
}
public static Collection<XmlFile> findNSFilesByURI(String namespace, final Project project, Module module) {
final List<IndexedRelevantResource<String, XsdNamespaceBuilder>>
resources = XmlNamespaceIndex.getResourcesByNamespace(namespace, project, module);
final PsiManager psiManager = PsiManager.getInstance(project);
return ContainerUtil.mapNotNull(resources, new NullableFunction<IndexedRelevantResource<String, XsdNamespaceBuilder>, XmlFile>() {
@Override
public XmlFile fun(IndexedRelevantResource<String, XsdNamespaceBuilder> resource) {
PsiFile file = psiManager.findFile(resource.getFile());
return file instanceof XmlFile ? (XmlFile)file : null;
}
});
}
@Nullable
public static XmlFile findXmlFile(PsiFile base, @NotNull String uri) {
PsiFile result = null;
if (ApplicationManager.getApplication().isUnitTestMode()) {
String data = base.getOriginalFile().getUserData(TEST_PATH);
if (data != null) {
String filePath = data + "/" + uri;
final VirtualFile path = StandardFileSystems.local().findFileByPath(filePath.replace(File.separatorChar, '/'));
if (path != null) {
result = base.getManager().findFile(path);
}
}
}
if (result == null) {
result = findRelativeFile(uri, base);
}
if (result instanceof XmlFile) {
return (XmlFile)result;
}
return null;
}
@Nullable
public static XmlToken getTokenOfType(PsiElement element, IElementType type) {
if (element == null) {
return null;
}
PsiElement[] children = element.getChildren();
for (PsiElement child : children) {
if (child instanceof XmlToken) {
XmlToken token = (XmlToken)child;
if (token.getTokenType() == type) {
return token;
}
}
}
return null;
}
public static boolean processXmlElements(XmlElement element, PsiElementProcessor processor, boolean deepFlag) {
return XmlPsiUtil.processXmlElements(element, processor, deepFlag);
}
public static boolean processXmlElements(XmlElement element, PsiElementProcessor processor, boolean deepFlag, boolean wideFlag) {
return XmlPsiUtil.processXmlElements(element, processor, deepFlag, wideFlag);
}
public static boolean processXmlElements(final XmlElement element,
final PsiElementProcessor processor,
final boolean deepFlag,
final boolean wideFlag,
final PsiFile baseFile) {
return XmlPsiUtil.processXmlElements(element, processor, deepFlag, wideFlag, baseFile);
}
public static boolean processXmlElements(final XmlElement element,
final PsiElementProcessor processor,
final boolean deepFlag,
final boolean wideFlag,
final PsiFile baseFile,
boolean processIncludes) {
return XmlPsiUtil.processXmlElements(element, processor, deepFlag, wideFlag, baseFile, processIncludes);
}
public static boolean processXmlElementChildren(final XmlElement element, final PsiElementProcessor processor, final boolean deepFlag) {
return XmlPsiUtil.processXmlElementChildren(element, processor, deepFlag);
}
public static boolean tagFromTemplateFramework(@NotNull final XmlTag tag) {
final String ns = tag.getNamespace();
return nsFromTemplateFramework(ns);
}
public static boolean nsFromTemplateFramework(final String ns) {
return XSLT_URI.equals(ns) || XINCLUDE_URI.equals(ns);
}
public static char getCharFromEntityRef(@NonNls @NotNull String text) {
try {
if (text.charAt(1) != '#') {
text = text.substring(1, text.length() - 1);
return XmlTagUtil.getCharacterByEntityName(text);
}
text = text.substring(2, text.length() - 1);
}
catch (StringIndexOutOfBoundsException e) {
LOG.error("Cannot parse ref: '" + text + "'", e);
}
try {
int code;
if (StringUtil.startsWithChar(text, 'x')) {
text = text.substring(1);
code = Integer.parseInt(text, 16);
}
else {
code = Integer.parseInt(text);
}
return (char)code;
}
catch (NumberFormatException e) {
return 0;
}
}
public static boolean attributeFromTemplateFramework(@NonNls final String name, final XmlTag tag) {
return "jsfc".equals(name) && isJsfHtmlScheme(tag);
}
@Nullable
public static String getTargetSchemaNsFromTag(@Nullable final XmlTag xmlTag) {
if (xmlTag == null) return null;
String targetNamespace = xmlTag.getAttributeValue(TARGET_NAMESPACE_ATTR_NAME, XML_SCHEMA_URI);
if (targetNamespace == null) targetNamespace = xmlTag.getAttributeValue(TARGET_NAMESPACE_ATTR_NAME, XML_SCHEMA_URI2);
if (targetNamespace == null) targetNamespace = xmlTag.getAttributeValue(TARGET_NAMESPACE_ATTR_NAME, XML_SCHEMA_URI3);
return targetNamespace;
}
@Nullable
public static XmlTag getSchemaSimpleContent(@NotNull XmlTag tag) {
XmlElementDescriptor descriptor = tag.getDescriptor();
if (descriptor instanceof XmlElementDescriptorImpl) {
final TypeDescriptor type = ((XmlElementDescriptorImpl)descriptor).getType(tag);
if (type instanceof ComplexTypeDescriptor) {
final XmlTag[] simpleContent = new XmlTag[1];
processXmlElements(((ComplexTypeDescriptor)type).getDeclaration(), new PsiElementProcessor() {
@Override
public boolean execute(@NotNull final PsiElement element) {
if (element instanceof XmlTag) {
final XmlTag tag = (XmlTag)element;
@NonNls final String s = ((XmlTag)element).getLocalName();
if ((s.equals(XSD_SIMPLE_CONTENT_TAG) ||
s.equals("restriction") && "string".equals(findLocalNameByQualifiedName(tag.getAttributeValue("base")))) &&
tag.getNamespace().equals(XML_SCHEMA_URI)) {
simpleContent[0] = tag;
return false;
}
}
return true;
}
}, true);
return simpleContent[0];
}
}
return null;
}
public static <T extends PsiElement> void doDuplicationCheckForElements(final T[] elements,
final Map<String, T> presentNames,
DuplicationInfoProvider<T> provider,
final Validator.ValidationHost host) {
for (T t : elements) {
final String name = provider.getName(t);
if (name == null) continue;
final String nameKey = provider.getNameKey(t, name);
if (presentNames.containsKey(nameKey)) {
final T psiElement = presentNames.get(nameKey);
final String message = XmlErrorMessages.message("duplicate.declaration", nameKey);
if (psiElement != null) {
presentNames.put(nameKey, null);
host.addMessage(provider.getNodeForMessage(psiElement), message, Validator.ValidationHost.ErrorType.ERROR);
}
host.addMessage(provider.getNodeForMessage(t), message, Validator.ValidationHost.ErrorType.ERROR);
}
else {
presentNames.put(nameKey, t);
}
}
}
public static String getEntityValue(final XmlEntityRef entityRef) {
final XmlEntityDecl decl = entityRef.resolve(entityRef.getContainingFile());
if (decl != null) {
final XmlAttributeValue valueElement = decl.getValueElement();
if (valueElement != null) {
final String value = valueElement.getValue();
if (value != null) {
return value;
}
}
}
return entityRef.getText();
}
public static boolean isAntFile(final PsiFile file) {
if (file instanceof XmlFile) {
final XmlFile xmlFile = (XmlFile)file;
final XmlDocument document = xmlFile.getDocument();
if (document != null) {
final XmlTag tag = document.getRootTag();
if (tag != null && "project".equals(tag.getName()) && tag.getContext() instanceof XmlDocument) {
if (tag.getAttributeValue("default") != null) {
return true;
}
VirtualFile vFile = xmlFile.getOriginalFile().getVirtualFile();
if (vFile != null && vFile.getUserData(ANT_FILE_SIGN) != null) {
return true;
}
}
}
}
return false;
}
private static boolean isJsfHtmlScheme(XmlTag tag) {
for (String jsfHtmlUri : JSF_HTML_URIS) {
if (tag.getNSDescriptor(jsfHtmlUri, true) != null) {
return true;
}
}
return false;
}
@Nullable
public static PsiFile findRelativeFile(String uri, PsiElement base) {
if (base instanceof PsiFile) {
PsiFile baseFile = (PsiFile)base;
VirtualFile file = UriUtil.findRelative(uri, baseFile.getOriginalFile());
if (file == null) return null;
return base.getManager().findFile(file);
}
else if (base instanceof PsiDirectory) {
PsiDirectory baseDir = (PsiDirectory)base;
VirtualFile file = UriUtil.findRelative(uri, baseDir);
if (file == null) return null;
return base.getManager().findFile(file);
}
return null;
}
@Nullable
public static String getCommentText(XmlComment comment) {
final PsiElement firstChild = comment.getFirstChild();
if (firstChild != null) {
final PsiElement nextSibling = firstChild.getNextSibling();
if (nextSibling instanceof XmlToken) {
final XmlToken token = (XmlToken)nextSibling;
if (token.getTokenType() == XmlTokenType.XML_COMMENT_CHARACTERS) {
return token.getText();
}
}
}
return null;
}
@Nullable
public static PsiElement findNamespaceDeclaration(XmlElement xmlElement, String nsName) {
while (!(xmlElement instanceof XmlTag) && xmlElement != null) {
final PsiElement parent = xmlElement.getParent();
if (!(parent instanceof XmlElement)) return null;
xmlElement = (XmlElement)parent;
}
if (xmlElement != null) {
XmlTag tag = (XmlTag)xmlElement;
while (tag != null) {
for (XmlAttribute attribute : tag.getAttributes()) {
if (attribute.isNamespaceDeclaration() && attribute.getLocalName().equals(nsName)) {
return attribute;
}
}
tag = tag.getParentTag();
}
}
return null;
}
public static void reformatTagStart(XmlTag tag) {
ASTNode child = XmlChildRole.START_TAG_END_FINDER.findChild(tag.getNode());
if (child == null) {
CodeStyleManager.getInstance(tag.getProject()).reformat(tag);
}
else {
CodeStyleManager.getInstance(tag.getProject())
.reformatRange(tag, tag.getTextRange().getStartOffset(), child.getTextRange().getEndOffset());
}
}
@Nullable
public static XmlElementDescriptor getDescriptorFromContext(@NotNull XmlTag tag) {
PsiElement parent = tag.getParent();
if (parent instanceof XmlTag) {
XmlTag parentTag = (XmlTag)parent;
final XmlElementDescriptor parentDescriptor = parentTag.getDescriptor();
if (parentDescriptor != null) {
return XmlExtension.getExtension(tag.getContainingFile()).getElementDescriptor(tag, parentTag, parentDescriptor);
}
}
return null;
}
public static void expandTag(@NotNull XmlTag tag) {
XmlTag newTag = XmlElementFactory.getInstance(tag.getProject()).createTagFromText('<' + tag.getName() + "></" + tag.getName() + '>');
ASTNode node = tag.getNode();
if (!(node instanceof CompositeElement)) return;
CompositeElement compositeElement = (CompositeElement)node;
final LeafElement emptyTagEnd = (LeafElement)XmlChildRole.EMPTY_TAG_END_FINDER.findChild(compositeElement);
if (emptyTagEnd == null) return;
compositeElement.removeChild(emptyTagEnd);
PsiElement[] children = newTag.getChildren();
compositeElement.addChildren(children[2].getNode(), null, null);
}
public static String getDefaultXhtmlNamespace(Project project) {
final String doctype = ExternalResourceManagerEx.getInstanceEx().getDefaultHtmlDoctype(project);
return Html5SchemaProvider.getHtml5SchemaLocation().equals(doctype)
? Html5SchemaProvider.getXhtml5SchemaLocation()
: doctype;
}
public static CharSequence getLocalName(final CharSequence tagName) {
int pos = StringUtil.indexOf(tagName, ':');
if (pos == -1) {
return tagName;
}
return tagName.subSequence(pos + 1, tagName.length());
}
public static boolean isStubBuilding() {
return BUILDING_DOM_STUBS.get();
}
/**
* add child to the parent according to DTD/Schema element ordering
*
* @return newly added child
*/
public static XmlTag addChildTag(XmlTag parent, XmlTag child) throws IncorrectOperationException {
return addChildTag(parent, child, -1);
}
public static XmlTag addChildTag(XmlTag parent, XmlTag child, int index) throws IncorrectOperationException {
// bug in PSI: cannot add child to <tag/>
if (parent.getSubTags().length == 0 && parent.getText().endsWith("/>")) {
final XmlElementFactory factory = XmlElementFactory.getInstance(parent.getProject());
final String name = parent.getName();
final String text = parent.getText();
final XmlTag tag = factory.createTagFromText(text.substring(0, text.length() - 2) + "></" + name + ">");
parent = (XmlTag)parent.replace(tag);
}
final XmlElementDescriptor parentDescriptor = parent.getDescriptor();
final XmlTag[] subTags = parent.getSubTags();
if (parentDescriptor == null || subTags.length == 0) return (XmlTag)parent.add(child);
int subTagNum = -1;
for (XmlElementDescriptor childElementDescriptor : parentDescriptor.getElementsDescriptors(parent)) {
final String childElementName = childElementDescriptor.getName();
int prevSubTagNum = subTagNum;
while (subTagNum < subTags.length - 1 && subTags[subTagNum + 1].getName().equals(childElementName)) {
subTagNum++;
}
if (childElementName.equals(child.getLocalName())) {
// insert child just after anchor
// insert into the position specified by index
subTagNum = index == -1 || index > subTagNum - prevSubTagNum ? subTagNum : prevSubTagNum + index;
return (XmlTag)(subTagNum == -1 ? parent.addBefore(child, subTags[0]) : parent.addAfter(child, subTags[subTagNum]));
}
}
return (XmlTag)parent.add(child);
}
/**
* @see XmlTag#getAttributeValue(String)
*/
@Nullable
@Deprecated
public static String getAttributeValue(XmlTag tag, String name) {
for (XmlAttribute attribute : tag.getAttributes()) {
if (name.equals(attribute.getName())) return attribute.getValue();
}
return null;
}
// Read the function name and parameter names to find out what this function does... :-)
@Nullable
public static XmlTag find(String subTag, String withValue, String forTag, XmlTag insideRoot) {
final XmlTag[] forTags = insideRoot.findSubTags(forTag);
for (XmlTag tag : forTags) {
final XmlTag[] allTags = tag.findSubTags(subTag);
for (XmlTag curTag : allTags) {
if (curTag.getName().equals(subTag) && curTag.getValue().getTrimmedText().equalsIgnoreCase(withValue)) {
return tag;
}
}
}
return null;
}
@Nullable
@NonNls
public static String[][] getDefaultNamespaces(final XmlDocument document) {
final XmlFile file = getContainingFile(document);
final XmlTag tag = document.getRootTag();
if (tag == null) return null;
@NotNull final XmlFileNSInfoProvider[] nsProviders = Extensions.getExtensions(XmlFileNSInfoProvider.EP_NAME);
if (file != null) {
NextProvider:
for (XmlFileNSInfoProvider nsProvider : nsProviders) {
final String[][] pairs = nsProvider.getDefaultNamespaces(file);
if (pairs != null && pairs.length > 0) {
for (final String[] nsMapping : pairs) {
if (nsMapping == null || nsMapping.length != 2 || nsMapping[0] == null || nsMapping[1] == null) {
LOG.debug("NSInfoProvider " + nsProvider + " gave wrong info about " + file.getVirtualFile());
continue NextProvider;
}
}
return pairs;
}
}
}
String namespace = getDtdUri(document);
if (namespace != null) {
boolean overrideNamespaceFromDocType = false;
if (file != null) {
for (XmlFileNSInfoProvider provider : nsProviders) {
try {
if (provider.overrideNamespaceFromDocType(file)) {
overrideNamespaceFromDocType = true;
break;
}
}
catch (AbstractMethodError ignored) {
}
}
}
if (!overrideNamespaceFromDocType) return new String[][]{new String[]{"", namespace}};
}
if ("taglib".equals(tag.getName())) {
return new String[][]{new String[]{"", TAGLIB_1_2_URI}};
}
if (file != null) {
final Language language = file.getLanguage();
if (language == HTMLLanguage.INSTANCE || language == XHTMLLanguage.INSTANCE) {
return new String[][]{new String[]{"", XHTML_URI}};
}
}
return null;
}
@Nullable
public static String getDtdUri(XmlDocument document) {
XmlProlog prolog = document.getProlog();
if (prolog != null) {
return getDtdUri(prolog.getDoctype());
}
return null;
}
@Nullable
public static String getDtdUri(XmlDoctype doctype) {
if (doctype != null) {
String docType = doctype.getDtdUri();
if (docType == null) {
final String publicId = doctype.getPublicId();
if (PsiTreeUtil.getParentOfType(doctype, XmlDocument.class) instanceof HtmlDocumentImpl &&
publicId != null && publicId.contains("-//W3C//DTD ")) {
return guessDtdByPublicId(publicId);
}
else if (HtmlUtil.isHtml5Doctype(doctype)) {
docType = doctype.getLanguage() instanceof HTMLLanguage
? Html5SchemaProvider.getHtml5SchemaLocation()
: Html5SchemaProvider.getXhtml5SchemaLocation();
}
}
return docType;
}
return null;
}
private static String guessDtdByPublicId(String id) {
if (id.contains("XHTML")) {
if (id.contains("1.1")) {
if (id.contains("Basic")) {
return "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd";
}
return "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd";
} else {
if (id.contains("Strict")) {
return "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
} else if (id.contains("Frameset")) {
return "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd";
} else if (id.contains("Transitional")) {
return "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";
}
}
} else if (id.contains("HTML")) {
if (id.contains("Strict")) {
return "http://www.w3.org/TR/html4/strict.dtd";
}
else if (id.contains("Frameset")) {
return "http://www.w3.org/TR/html4/frameset.dtd";
}
return HTML4_LOOSE_URI;
}
return null;
}
private static void computeTag(XmlTag tag,
final Map<String, List<String>> tagsMap,
final Map<String, List<MyAttributeInfo>> attributesMap,
final boolean processIncludes) {
if (tag == null) {
return;
}
final String tagName = tag.getName();
List<MyAttributeInfo> list = attributesMap.get(tagName);
if (list == null) {
list = new ArrayList<MyAttributeInfo>();
final XmlAttribute[] attributes = tag.getAttributes();
for (final XmlAttribute attribute : attributes) {
list.add(new MyAttributeInfo(attribute.getName()));
}
}
else {
final XmlAttribute[] attributes = tag.getAttributes();
ContainerUtil.sort(list);
Arrays.sort(attributes, new Comparator<XmlAttribute>() {
@Override
public int compare(XmlAttribute attr1, XmlAttribute attr2) {
return attr1.getName().compareTo(attr2.getName());
}
});
final Iterator<MyAttributeInfo> iter = list.iterator();
list = new ArrayList<MyAttributeInfo>();
int index = 0;
while (iter.hasNext()) {
final MyAttributeInfo info = iter.next();
boolean requiredFlag = false;
while (attributes.length > index) {
if (info.compareTo(attributes[index]) != 0) {
if (info.compareTo(attributes[index]) < 0) {
break;
}
if (attributes[index].getValue() != null) list.add(new MyAttributeInfo(attributes[index].getName(), false));
index++;
}
else {
requiredFlag = true;
index++;
break;
}
}
info.myRequired &= requiredFlag;
list.add(info);
}
while (attributes.length > index) {
if (attributes[index].getValue() != null) {
list.add(new MyAttributeInfo(attributes[index++].getName(), false));
}
else {
index++;
}
}
}
attributesMap.put(tagName, list);
final List<String> tags = tagsMap.get(tagName) != null ? tagsMap.get(tagName) : new ArrayList<String>();
tagsMap.put(tagName, tags);
PsiFile file = tag.isValid() ? tag.getContainingFile() : null;
processXmlElements(tag, new FilterElementProcessor(XmlTagFilter.INSTANCE) {
@Override
public void add(PsiElement element) {
XmlTag tag = (XmlTag)element;
if (!tags.contains(tag.getName())) {
tags.add(tag.getName());
}
computeTag(tag, tagsMap, attributesMap, processIncludes);
}
}, false, false, file, processIncludes);
/*tag.processElements(new FilterElementProcessor(XmlTagFilter.INSTANCE) {
public void add(PsiElement element) {
XmlTag tag = (XmlTag)element;
if (!tags.contains(tag.getName())) {
tags.add(tag.getName());
}
computeTag(tag, tagsMap, attributesMap);
}
}, tag);*/
}
@Nullable
public static XmlElementDescriptor findXmlDescriptorByType(final XmlTag xmlTag) {
return findXmlDescriptorByType(xmlTag, null);
}
@Nullable
public static XmlElementDescriptor findXmlDescriptorByType(final XmlTag xmlTag, @Nullable XmlTag context) {
String type = xmlTag.getAttributeValue("type", XML_SCHEMA_INSTANCE_URI);
if (type == null) {
String ns = xmlTag.getNamespace();
if (ourSchemaUrisList.indexOf(ns) >= 0) {
type = xmlTag.getAttributeValue("type", null);
}
}
XmlElementDescriptor elementDescriptor = null;
if (type != null) {
final String namespaceByPrefix = findNamespaceByPrefix(findPrefixByQualifiedName(type), xmlTag);
XmlNSDescriptor typeDecr = xmlTag.getNSDescriptor(namespaceByPrefix, true);
if (typeDecr == null && namespaceByPrefix.isEmpty()) {
if (context != null) typeDecr = context.getNSDescriptor("", true);
if (typeDecr == null) {
final PsiFile containingFile = xmlTag.getContainingFile();
if (containingFile instanceof XmlFile) {
final XmlDocument document = ((XmlFile)containingFile).getDocument();
if (document != null) typeDecr = (XmlNSDescriptor)document.getMetaData();
}
}
}
if (typeDecr instanceof XmlNSDescriptorImpl) {
final XmlNSDescriptorImpl schemaDescriptor = (XmlNSDescriptorImpl)typeDecr;
elementDescriptor = schemaDescriptor.getDescriptorByType(type, xmlTag);
}
}
return elementDescriptor;
}
public static boolean collectEnumerationValues(final XmlTag element, final HashSet<String> variants) {
return processEnumerationValues(element, new Processor<XmlTag>() {
@Override
public boolean process(XmlTag xmlTag) {
variants.add(xmlTag.getAttributeValue(VALUE_ATTR_NAME));
return true;
}
});
}
/**
* @return true if enumeration is exhaustive
*/
public static boolean processEnumerationValues(final XmlTag element, final Processor<XmlTag> tagProcessor) {
boolean exhaustiveEnum = true;
for (final XmlTag tag : element.getSubTags()) {
@NonNls final String localName = tag.getLocalName();
if (localName.equals(ENUMERATION_TAG_NAME)) {
final String attributeValue = tag.getAttributeValue(VALUE_ATTR_NAME);
if (attributeValue != null) {
if (!tagProcessor.process(tag)) {
return exhaustiveEnum;
}
}
}
else if (localName.equals("union")) {
exhaustiveEnum = false;
processEnumerationValues(tag, tagProcessor);
}
else if (!doNotVisitTags.contains(localName)) {
// don't go into annotation
exhaustiveEnum &= processEnumerationValues(tag, tagProcessor);
}
}
return exhaustiveEnum;
}
/**
* @param xmlTag
* @param localName
* @param namespace
* @param bodyText pass null to create collapsed tag, empty string means creating expanded one
* @param enforceNamespacesDeep
* @return
*/
public static XmlTag createChildTag(final XmlTag xmlTag,
String localName,
String namespace,
@Nullable String bodyText,
boolean enforceNamespacesDeep) {
String qname;
final String prefix = xmlTag.getPrefixByNamespace(namespace);
if (prefix != null && !prefix.isEmpty()) {
qname = prefix + ":" + localName;
}
else {
qname = localName;
}
try {
String tagStart = qname + (!StringUtil.isEmpty(namespace) && xmlTag.getPrefixByNamespace(namespace) == null &&
!(StringUtil.isEmpty(xmlTag.getNamespacePrefix()) && namespace.equals(xmlTag.getNamespace()))
? " xmlns=\"" + namespace + "\""
: "");
Language language = xmlTag.getLanguage();
if (!(language instanceof HTMLLanguage)) language = XMLLanguage.INSTANCE;
XmlTag retTag;
if (bodyText != null) {
retTag = XmlElementFactory.getInstance(xmlTag.getProject())
.createTagFromText("<" + tagStart + ">" + bodyText + "</" + qname + ">", language);
if (enforceNamespacesDeep) {
retTag.acceptChildren(new XmlRecursiveElementVisitor() {
@Override
public void visitXmlTag(XmlTag tag) {
final String namespacePrefix = tag.getNamespacePrefix();
if (namespacePrefix.isEmpty()) {
String qname;
if (prefix != null && !prefix.isEmpty()) {
qname = prefix + ":" + tag.getLocalName();
}
else {
qname = tag.getLocalName();
}
try {
tag.setName(qname);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
super.visitXmlTag(tag);
}
});
}
}
else {
retTag = XmlElementFactory.getInstance(xmlTag.getProject()).createTagFromText("<" + tagStart + "/>", language);
}
return retTag;
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
return null;
}
@Nullable
public static Pair<XmlTagChild, XmlTagChild> findTagChildrenInRange(final PsiFile file, int startOffset, int endOffset) {
PsiElement elementAtStart = file.findElementAt(startOffset);
PsiElement elementAtEnd = file.findElementAt(endOffset - 1);
if (elementAtStart instanceof PsiWhiteSpace) {
startOffset = elementAtStart.getTextRange().getEndOffset();
elementAtStart = file.findElementAt(startOffset);
}
if (elementAtEnd instanceof PsiWhiteSpace) {
endOffset = elementAtEnd.getTextRange().getStartOffset();
elementAtEnd = file.findElementAt(endOffset - 1);
}
if (elementAtStart == null || elementAtEnd == null) return null;
XmlTagChild first = PsiTreeUtil.getParentOfType(elementAtStart, XmlTagChild.class);
if (first == null) return null;
if (first.getTextRange().getStartOffset() != startOffset) {
//Probably 'first' starts with whitespace
PsiElement elementAt = file.findElementAt(first.getTextRange().getStartOffset());
if (!(elementAt instanceof PsiWhiteSpace) || elementAt.getTextRange().getEndOffset() != startOffset) return null;
}
XmlTagChild last = first;
while (last != null && last.getTextRange().getEndOffset() < endOffset) {
last = PsiTreeUtil.getNextSiblingOfType(last, XmlTagChild.class);
}
if (last == null) return null;
if (last.getTextRange().getEndOffset() != elementAtEnd.getTextRange().getEndOffset()) {
//Probably 'last' ends with whitespace
PsiElement elementAt = file.findElementAt(last.getTextRange().getEndOffset() - 1);
if (!(elementAt instanceof PsiWhiteSpace) || elementAt.getTextRange().getStartOffset() != endOffset) {
return null;
}
}
return Pair.create(first, last);
}
public static boolean isSimpleValue(@NotNull final String unquotedValue, final PsiElement context) {
for (int i = 0; i < unquotedValue.length(); ++i) {
final char ch = unquotedValue.charAt(i);
if (!Character.isJavaIdentifierPart(ch) && ch != ':' && ch != '-') {
final XmlFile file = PsiTreeUtil.getParentOfType(context, XmlFile.class);
if (file != null) {
XmlTag tag = file.getRootTag();
return tag != null && !tagFromTemplateFramework(tag);
}
}
}
return true;
}
public static boolean toCode(String str) {
for (int i = 0; i < str.length(); i++) {
if (toCode(str.charAt(i))) return true;
}
return false;
}
public static boolean toCode(char ch) {
return "<&>\u00a0".indexOf(ch) >= 0;
}
@Nullable
public static PsiNamedElement findRealNamedElement(@NotNull final PsiNamedElement _element) {
PsiElement currentElement = _element;
final XmlEntityRef lastEntityRef = PsiTreeUtil.getParentOfType(currentElement, XmlEntityRef.class);
while (!(currentElement instanceof XmlFile)) {
PsiElement dependingElement = currentElement.getUserData(XmlElement.DEPENDING_ELEMENT);
if (dependingElement == null) dependingElement = currentElement.getContext();
currentElement = dependingElement;
if (dependingElement == null) break;
}
if (currentElement != null) {
final String name = _element.getName();
if (_element instanceof XmlEntityDecl) {
final XmlEntityDecl cachedEntity = XmlEntityCache.getCachedEntity((PsiFile)currentElement, name);
if (cachedEntity != null) return cachedEntity;
}
final PsiNamedElement[] result = new PsiNamedElement[1];
processXmlElements((XmlFile)currentElement, new PsiElementProcessor() {
@Override
public boolean execute(@NotNull final PsiElement element) {
if (element instanceof PsiNamedElement) {
final String elementName = ((PsiNamedElement)element).getName();
if (elementName.equals(name) && _element.getClass().isInstance(element)
|| lastEntityRef != null && element instanceof XmlEntityDecl &&
elementName.equals(lastEntityRef.getText().substring(1, lastEntityRef.getTextLength() - 1))) {
result[0] = (PsiNamedElement)element;
return false;
}
}
return true;
}
}, true);
return result[0];
}
return null;
}
public static int getPrefixLength(@NotNull final String s) {
if (s.startsWith(TAG_DIR_NS_PREFIX)) return TAG_DIR_NS_PREFIX.length();
if (s.startsWith(FILE)) return FILE.length();
if (s.startsWith(CLASSPATH)) return CLASSPATH.length();
return 0;
}
public static boolean isUrlText(final String s, Project project) {
final boolean surelyUrl = HtmlUtil.hasHtmlPrefix(s) || s.startsWith(URN);
if (surelyUrl) return surelyUrl;
int protocolIndex = s.indexOf(":/");
if (protocolIndex > 1 && !s.regionMatches(0,"classpath",0,protocolIndex)) return true;
return ExternalResourceManager.getInstance().getResourceLocation(s, project) != s;
}
public static String generateDocumentDTD(XmlDocument doc, boolean full) {
final Map<String, List<String>> tags = new LinkedHashMap<String, List<String>>();
final Map<String, List<MyAttributeInfo>> attributes = new LinkedHashMap<String, List<MyAttributeInfo>>();
try {
XmlEntityRefImpl.setNoEntityExpandOutOfDocument(doc, true);
final XmlTag rootTag = doc.getRootTag();
computeTag(rootTag, tags, attributes, full);
// For supporting not well-formed XML
for (PsiElement element = rootTag != null ? rootTag.getNextSibling() : null; element != null; element = element.getNextSibling()) {
if (element instanceof XmlTag) {
computeTag((XmlTag)element, tags, attributes, full);
}
}
}
finally {
XmlEntityRefImpl.setNoEntityExpandOutOfDocument(doc, false);
}
final StringBuilder buffer = new StringBuilder();
for (final String tagName : tags.keySet()) {
buffer.append(generateElementDTD(tagName, tags.get(tagName), attributes.get(tagName)));
}
return buffer.toString();
}
public static String generateElementDTD(String name, List<String> tags, List<MyAttributeInfo> attributes) {
if (name == null || "".equals(name)) return "";
if (name.contains(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED)) return "";
@NonNls final StringBuilder buffer = new StringBuilder();
buffer.append("<!ELEMENT ").append(name).append(" ");
if (tags.isEmpty()) {
buffer.append("(#PCDATA)>\n");
}
else {
buffer.append("(");
final Iterator<String> iter = tags.iterator();
while (iter.hasNext()) {
final String tagName = iter.next();
buffer.append(tagName);
if (iter.hasNext()) {
buffer.append("|");
}
else {
buffer.append(")*");
}
}
buffer.append(">\n");
}
if (!attributes.isEmpty()) {
buffer.append("<!ATTLIST ").append(name);
for (final MyAttributeInfo info : attributes) {
buffer.append("\n ").append(generateAttributeDTD(info));
}
buffer.append(">\n");
}
return buffer.toString();
}
private static String generateAttributeDTD(MyAttributeInfo info) {
if (info.myName.contains(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED)) return "";
return info.myName + " " + "CDATA" + (info.myRequired ? " #REQUIRED" : " #IMPLIED");
}
@Nullable
public static String trimLeadingSpacesInMultilineTagValue(@NonNls String tagValue) {
return tagValue == null ? null : tagValue.replaceAll("\n\\s*", "\n");
}
public static String findNamespaceByPrefix(final String prefix, XmlTag contextTag) {
return contextTag.getNamespaceByPrefix(prefix);
}
@NotNull
public static String findPrefixByQualifiedName(@NotNull String name) {
final int prefixEnd = name.indexOf(':');
if (prefixEnd > 0) {
return name.substring(0, prefixEnd);
}
return "";
}
@Nullable
public static String findLocalNameByQualifiedName(String name) {
return name == null ? null : name.substring(name.indexOf(':') + 1);
}
public static XmlFile getContainingFile(PsiElement element) {
while (!(element instanceof XmlFile) && element != null) {
final PsiElement context = element.getContext();
if (context == null) {
//todo Dmitry Avdeev: either XmlExtension should work on any PsiFile (not just XmlFile), or you need to handle elements from JspJavaFile in some other way
final XmlExtension extension = XmlExtension.getExtensionByElement(element);
if (extension != null) {
element = extension.getContainingFile(element);
}
}
else {
if (element == context) {
LOG.error("Context==element: " + element.getClass());
return null;
}
element = context;
}
}
return (XmlFile)element;
}
@Nullable
public static String getSubTagValue(XmlTag tag, final String subTagName) {
final XmlTag subTag = tag.findFirstSubTag(subTagName);
if (subTag != null) {
return subTag.getValue().getTrimmedText();
}
return null;
}
public static int getStartOffsetInFile(XmlTag xmlTag) {
int off = 0;
while (true) {
off += xmlTag.getStartOffsetInParent();
final PsiElement parent = xmlTag.getParent();
if (!(parent instanceof XmlTag)) break;
xmlTag = (XmlTag)parent;
}
return off;
}
public static XmlElement setNewValue(XmlElement tag, String value) throws IncorrectOperationException {
if (tag instanceof XmlTag) {
((XmlTag)tag).getValue().setText(value);
return tag;
}
else if (tag instanceof XmlAttribute) {
XmlAttribute attr = (XmlAttribute)tag;
attr.setValue(value);
return attr;
}
else {
throw new IncorrectOperationException();
}
}
public static String decode(@NonNls String text) {
if (text.isEmpty()) return text;
if (text.charAt(0) != '&' || text.length() < 3) {
if (text.indexOf('<') < 0 && text.indexOf('>') < 0) return text;
return text.replaceAll("<!\\[CDATA\\[", "").replaceAll("\\]\\]>", "");
}
if (text.equals("&lt;")) {
return "<";
}
if (text.equals("&gt;")) {
return ">";
}
if (text.equals("&nbsp;")) {
return "\u00a0";
}
if (text.equals("&amp;")) {
return "&";
}
if (text.equals("&apos;")) {
return "'";
}
if (text.equals("&quot;")) {
return "\"";
}
if (text.startsWith("&quot;") && text.endsWith("&quot;")) {
return "\"" + text.substring(6, text.length() - 6) + "\"";
}
if (text.startsWith("&#")) {
text = text.substring(3, text.length() - 1);
try {
return String.valueOf((char)Integer.parseInt(text));
}
catch (NumberFormatException e) {
// ignore
}
}
return text;
}
public static String unescape(String text) {
return StringUtil.unescapeXml(text);
}
public static String escape(String text) {
return StringUtil.escapeXml(text);
}
@Nullable
public static String extractXmlEncodingFromProlog(final byte[] content) {
return XmlCharsetDetector.extractXmlEncodingFromProlog(content);
}
@Nullable
public static String extractXmlEncodingFromProlog(String text) {
return XmlCharsetDetector.extractXmlEncodingFromProlog(text);
}
public static void registerXmlAttributeValueReferenceProvider(PsiReferenceRegistrar registrar,
@Nullable @NonNls String[] attributeNames,
@Nullable ElementFilter elementFilter,
@NotNull PsiReferenceProvider provider) {
registerXmlAttributeValueReferenceProvider(registrar, attributeNames, elementFilter, true, provider);
}
public static void registerXmlAttributeValueReferenceProvider(PsiReferenceRegistrar registrar,
@Nullable @NonNls String[] attributeNames,
@Nullable ElementFilter elementFilter,
boolean caseSensitive,
@NotNull PsiReferenceProvider provider) {
registerXmlAttributeValueReferenceProvider(registrar, attributeNames, elementFilter, caseSensitive, provider,
PsiReferenceRegistrar.DEFAULT_PRIORITY);
}
public static void registerXmlAttributeValueReferenceProvider(PsiReferenceRegistrar registrar,
@Nullable @NonNls String[] attributeNames,
@Nullable ElementFilter elementFilter,
boolean caseSensitive,
@NotNull PsiReferenceProvider provider,
double priority) {
if (attributeNames == null) {
registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue().and(new FilterPattern(elementFilter)), provider, priority);
return;
}
final StringPattern namePattern = caseSensitive
? StandardPatterns.string().oneOf(attributeNames)
: StandardPatterns.string().oneOfIgnoreCase(attributeNames);
registrar
.registerReferenceProvider(XmlPatterns.xmlAttributeValue().withLocalName(namePattern).and(new FilterPattern(elementFilter)), provider,
priority);
}
public static void registerXmlTagReferenceProvider(PsiReferenceRegistrar registrar,
@NonNls String[] names,
@Nullable ElementFilter elementFilter,
boolean caseSensitive,
@NotNull PsiReferenceProvider provider) {
if (names == null) {
registrar.registerReferenceProvider(XmlPatterns.xmlTag().and(new FilterPattern(elementFilter)), provider,
PsiReferenceRegistrar.DEFAULT_PRIORITY);
return;
}
final StringPattern namePattern =
caseSensitive ? StandardPatterns.string().oneOf(names) : StandardPatterns.string().oneOfIgnoreCase(names);
registrar.registerReferenceProvider(XmlPatterns.xmlTag().withLocalName(namePattern).and(new FilterPattern(elementFilter)), provider,
PsiReferenceRegistrar.DEFAULT_PRIORITY);
}
public interface DuplicationInfoProvider<T extends PsiElement> {
@Nullable
String getName(@NotNull T t);
@NotNull
String getNameKey(@NotNull T t, @NotNull String name);
@NotNull
PsiElement getNodeForMessage(@NotNull T t);
}
private static class MyAttributeInfo implements Comparable {
boolean myRequired = true;
String myName = null;
MyAttributeInfo(String name) {
myName = name;
}
MyAttributeInfo(String name, boolean flag) {
myName = name;
myRequired = flag;
}
@Override
public int compareTo(Object o) {
if (o instanceof MyAttributeInfo) {
return myName.compareTo(((MyAttributeInfo)o).myName);
}
else if (o instanceof XmlAttribute) {
return myName.compareTo(((XmlAttribute)o).getName());
}
return -1;
}
}
}