mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
825 lines
28 KiB
Java
825 lines
28 KiB
Java
package com.intellij.xml.util;
|
|
|
|
import com.intellij.codeInsight.completion.CompletionUtil;
|
|
import com.intellij.j2ee.openapi.ex.ExternalResourceManagerEx;
|
|
import com.intellij.openapi.application.ApplicationManager;
|
|
import com.intellij.openapi.util.Key;
|
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
|
import com.intellij.openapi.vfs.VirtualFile;
|
|
import com.intellij.openapi.fileTypes.StdFileTypes;
|
|
import com.intellij.openapi.fileTypes.FileType;
|
|
import com.intellij.psi.PsiElement;
|
|
import com.intellij.psi.PsiElementFactory;
|
|
import com.intellij.psi.PsiFile;
|
|
import com.intellij.psi.meta.PsiMetaData;
|
|
import com.intellij.psi.tree.IElementType;
|
|
import com.intellij.psi.filters.ClassFilter;
|
|
import com.intellij.psi.scope.processor.FilterElementProcessor;
|
|
import com.intellij.psi.search.PsiElementProcessor;
|
|
import com.intellij.psi.search.PsiSearchScopeUtil;
|
|
import com.intellij.psi.search.SearchScope;
|
|
import com.intellij.psi.search.GlobalSearchScope;
|
|
import com.intellij.psi.util.CachedValue;
|
|
import com.intellij.psi.util.CachedValueProvider;
|
|
import com.intellij.psi.util.PsiUtil;
|
|
import com.intellij.psi.xml.*;
|
|
import com.intellij.util.IncorrectOperationException;
|
|
import com.intellij.util.ArrayUtil;
|
|
import com.intellij.xml.XmlElementDescriptor;
|
|
import com.intellij.xml.impl.ant.AntPropertyDeclaration;
|
|
|
|
import java.io.File;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* @author Mike
|
|
*/
|
|
public class XmlUtil {
|
|
public static final String TAGLIB_1_1_URI = "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd";
|
|
public static final String TAGLIB_1_2_URI = "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd";
|
|
public static final String TAGLIB_1_2_a_URI = "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_2.dtd";
|
|
public static final String TAGLIB_1_2_b_URI = "http://java.sun.com/JSP/TagLibraryDescriptor";
|
|
public static final String TAGLIB_2_0_URI = "http://java.sun.com/xml/ns/j2ee";
|
|
|
|
public static final String XML_SCHEMA_URI = "http://www.w3.org/2001/XMLSchema";
|
|
public static final String XML_SCHEMA_URI2 = "http://www.w3.org/1999/XMLSchema";
|
|
public static final String XML_SCHEMA_URI3 = "http://www.w3.org/2000/10/XMLSchema";
|
|
public static final String XML_SCHEMA_INSTANCE_URI = "http://www.w3.org/2001/XMLSchema-instance";
|
|
public static final String ANT_URI = "http://ant.apache.org/schema.xsd";
|
|
public static final String XHTML_URI = "http://www.w3.org/1999/xhtml";
|
|
public static final String EMPTY_NAMESPACE = "";
|
|
public static final Key<String> TEST_PATH = Key.create("TEST PATH");
|
|
public static final String JSP_NAMESPACE = "http://java.sun.com/JSP/Page";
|
|
public static final String ALL_NAMESPACE = "http://www.intellij.net/ns/all";
|
|
|
|
static {
|
|
PsiSearchScopeUtil.registerAccessScopeHandler(StdFileTypes.DTD, new PsiSearchScopeUtil.AccessScopeHandler() {
|
|
public SearchScope getAccessScope(PsiElement element) {
|
|
return GlobalSearchScope.projectScope(element.getProject());
|
|
}
|
|
});
|
|
}
|
|
|
|
public static String getSchemaLocation(XmlTag tag, String namespace) {
|
|
final String uri = ExternalResourceManagerEx.getInstanceEx().getResourceLocation(namespace);
|
|
if (uri != null && !uri.equals(namespace)) return uri;
|
|
|
|
while (true) {
|
|
if ("".equals(namespace)) {
|
|
final String attributeValue = tag.getAttributeValue("noNamespaceSchemaLocation", XML_SCHEMA_INSTANCE_URI);
|
|
if(attributeValue != null) return attributeValue;
|
|
}
|
|
else {
|
|
String schemaLocation = tag.getAttributeValue("schemaLocation", XML_SCHEMA_INSTANCE_URI);
|
|
if (schemaLocation != null) {
|
|
int start = schemaLocation.indexOf(namespace);
|
|
if (start >= 0) {
|
|
start += namespace.length();
|
|
final StringTokenizer tokenizer = new StringTokenizer(schemaLocation.substring(start + 1));
|
|
if (tokenizer.hasMoreTokens()) {
|
|
return tokenizer.nextToken();
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (tag.getParent() instanceof XmlTag) {
|
|
tag = (XmlTag)tag.getParent();
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static String findNamespacePrefixByURI(XmlFile file, String uri) {
|
|
if (file == null) return null;
|
|
final XmlDocument document = file.getDocument();
|
|
if (document == null) return null;
|
|
final XmlTag tag = document.getRootTag();
|
|
if (tag == null) return null;
|
|
XmlAttribute[] attributes = tag.getAttributes();
|
|
|
|
|
|
for (int i = 0; i < attributes.length; i++) {
|
|
XmlAttribute attribute = attributes[i];
|
|
if (attribute.getName().startsWith("xmlns:") &&
|
|
attribute.getValue().equals(uri)) {
|
|
String ns = attribute.getName().substring("xmlns:".length());
|
|
return ns;
|
|
}
|
|
if ("xmlns".equals(attribute.getName()) && attribute.getValue().equals(uri)) 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 (int i = 0; i < attributes.length; i++) {
|
|
XmlAttribute attribute = attributes[i];
|
|
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 result.toArray(new String[result.size()]);
|
|
}
|
|
|
|
public static String getXsiNamespace(XmlFile file) {
|
|
return findNamespacePrefixByURI(file, XML_SCHEMA_INSTANCE_URI);
|
|
}
|
|
|
|
public static XmlFile findXmlFile(PsiFile base, String uri) {
|
|
PsiFile result = null;
|
|
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
|
String data = base.getUserData(TEST_PATH);
|
|
|
|
if (data == null && base.getOriginalFile() != null) {
|
|
data = base.getOriginalFile().getUserData(TEST_PATH);
|
|
}
|
|
if (data != null) {
|
|
String filePath = data + "/" + uri;
|
|
final VirtualFile path = LocalFileSystem.getInstance().findFileByPath(filePath.replace(File.separatorChar, '/'));
|
|
if (path != null) {
|
|
result = base.getManager().findFile(path);
|
|
}
|
|
}
|
|
}
|
|
if (result == null) {
|
|
result = PsiUtil.findRelativeFile(uri, base);
|
|
}
|
|
|
|
if (result instanceof XmlFile) {
|
|
XmlFile xmlFile = (XmlFile)result;
|
|
return xmlFile;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static XmlToken getTokenOfType(PsiElement element, IElementType type) {
|
|
if (element == null) {
|
|
return null;
|
|
}
|
|
|
|
PsiElement[] children = element.getChildren();
|
|
|
|
for (int i = 0; i < children.length; i++) {
|
|
PsiElement child = children[i];
|
|
|
|
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 processXmlElements(element, processor, deepFlag, false);
|
|
}
|
|
|
|
public static boolean processXmlElements(XmlElement element, PsiElementProcessor processor, boolean deepFlag, boolean wideFlag) {
|
|
if (element == null) return true;
|
|
PsiFile baseFile = element.getContainingFile();
|
|
return _processXmlElements(element, processor, baseFile, deepFlag, wideFlag);
|
|
}
|
|
|
|
private static boolean _processXmlElements(PsiElement element,
|
|
PsiElementProcessor processor,
|
|
PsiFile targetFile,
|
|
boolean deepFlag,
|
|
boolean wideFlag) {
|
|
if (deepFlag) if (!processor.execute(element)) return false;
|
|
|
|
if (element instanceof XmlEntityRef) {
|
|
XmlEntityRef ref = (XmlEntityRef)element;
|
|
|
|
PsiElement newElement = parseEntityRef(targetFile, ref, true);
|
|
if (newElement == null) return true;
|
|
|
|
while (newElement != null) {
|
|
if (!processElement(newElement, processor, targetFile, deepFlag, wideFlag)) return false;
|
|
newElement = newElement.getNextSibling();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
|
|
if (!processElement(child, processor, targetFile, deepFlag, wideFlag) && !wideFlag) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static boolean processElement(PsiElement child,
|
|
PsiElementProcessor processor,
|
|
PsiFile targetFile,
|
|
boolean deepFlag,
|
|
boolean wideFlag) {
|
|
if (deepFlag) {
|
|
if (!_processXmlElements(child, processor, targetFile, true, wideFlag)) {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
if (child instanceof XmlEntityRef) {
|
|
if (!_processXmlElements(child, processor, targetFile, false, wideFlag)) return false;
|
|
}
|
|
else if (!processor.execute(child)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static PsiElement parseEntityRef(PsiFile targetFile, XmlEntityRef ref, boolean cacheValue) {
|
|
int type = getContextType(ref);
|
|
|
|
{
|
|
final XmlEntityDecl entityDecl = ref.resolve(targetFile);
|
|
if (entityDecl != null) return parseEntityDecl(entityDecl, targetFile, type, cacheValue, ref);
|
|
}
|
|
|
|
PsiElement e = ref;
|
|
while (e != null) {
|
|
if (e.getUserData(XmlElement.ORIGINAL_ELEMENT) != null) {
|
|
e = (PsiElement)e.getUserData(XmlElement.ORIGINAL_ELEMENT);
|
|
final PsiFile f = e.getContainingFile();
|
|
if (f != null) {
|
|
final XmlEntityDecl entityDecl = ref.resolve(targetFile);
|
|
if (entityDecl != null) return parseEntityDecl(entityDecl, targetFile, type, cacheValue, ref);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
if (e instanceof PsiFile) {
|
|
PsiFile refFile = (PsiFile)e;
|
|
final XmlEntityDecl entityDecl = ref.resolve(refFile);
|
|
if (entityDecl != null) return parseEntityDecl(entityDecl, targetFile, type, cacheValue, ref);
|
|
break;
|
|
}
|
|
|
|
e = e.getParent();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static int getContextType(XmlEntityRef ref) {
|
|
int type = XmlEntityDecl.CONTEXT_GENERIC_XML;
|
|
PsiElement temp = ref;
|
|
while (temp != null) {
|
|
if (temp instanceof XmlAttributeDecl) {
|
|
type = XmlEntityDecl.CONTEXT_ATTRIBUTE_SPEC;
|
|
}
|
|
else if (temp instanceof XmlElementDecl) {
|
|
type = XmlEntityDecl.CONTEXT_ELEMENT_CONTENT_SPEC;
|
|
}
|
|
else if (temp instanceof XmlAttlistDecl) {
|
|
type = XmlEntityDecl.CONTEXT_ATTLIST_SPEC;
|
|
}
|
|
else if (temp instanceof XmlEntityDecl) {
|
|
type = XmlEntityDecl.CONTEXT_ENTITY_DECL_CONTENT;
|
|
}
|
|
else if (temp instanceof XmlEnumeratedType) {
|
|
type = XmlEntityDecl.CONTEXT_ENUMERATED_TYPE;
|
|
}
|
|
else {
|
|
temp = temp.getContext();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
private static final Key<CachedValue> PARSED_DECL_KEY = Key.create("PARSED_DECL_KEY");
|
|
|
|
private static PsiElement parseEntityDecl(final XmlEntityDecl entityDecl,
|
|
final PsiFile targetFile,
|
|
final int type,
|
|
boolean cacheValue,
|
|
final XmlEntityRef entityRef) {
|
|
if (!cacheValue) return entityDecl.parse(targetFile, type, entityRef);
|
|
|
|
CachedValue value = entityRef.getUserData(PARSED_DECL_KEY);
|
|
// return entityDecl.parse(targetFile, type);
|
|
|
|
if (value == null) {
|
|
value = entityDecl.getManager().getCachedValuesManager().createCachedValue(new CachedValueProvider() {
|
|
public CachedValueProvider.Result compute() {
|
|
final PsiElement res = entityDecl.parse(targetFile, type, entityRef);
|
|
if (res == null) return new Result(res, new Object[]{targetFile});
|
|
return new CachedValueProvider.Result(res, new Object[]{res.getUserData(XmlElement.DEPENDING_ELEMENT), entityDecl, targetFile, entityRef});
|
|
}
|
|
}, false);
|
|
entityRef.putUserData(PARSED_DECL_KEY, value);
|
|
}
|
|
|
|
return (PsiElement)value.getValue();
|
|
}
|
|
|
|
/**
|
|
* 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 PsiElementFactory factory = parent.getManager().getElementFactory();
|
|
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);
|
|
final XmlElementDescriptor[] childElementDescriptors = parentDescriptor.getElementsDescriptors(parent);
|
|
int subTagNum = -1;
|
|
for (int i = 0; i < childElementDescriptors.length; i++) {
|
|
XmlElementDescriptor childElementDescriptor = childElementDescriptors[i];
|
|
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);
|
|
}
|
|
|
|
public static String getAttributeValue(XmlTag tag, String name) {
|
|
final XmlAttribute[] attributes = tag.getAttributes();
|
|
for (int i = 0; i < attributes.length; i++) {
|
|
XmlAttribute attribute = attributes[i];
|
|
if (name.equals(attribute.getName())) return attribute.getValue();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static XmlTag findOnAnyLevel(XmlTag root, String[] chain) {
|
|
XmlTag curTag = root;
|
|
for (int i = 0; i < chain.length; i++) {
|
|
String s = chain[i];
|
|
curTag = curTag.findFirstSubTag(s);
|
|
if (curTag == null) return null;
|
|
}
|
|
|
|
return curTag;
|
|
}
|
|
|
|
public static XmlTag findSubTag(XmlTag rootTag, String path) {
|
|
String[] pathElements = path.split("/");
|
|
|
|
XmlTag curTag = rootTag;
|
|
for (int i = 0; i < pathElements.length; i++) {
|
|
String curTagName = pathElements[i];
|
|
curTag = curTag.findFirstSubTag(curTagName);
|
|
if (curTag == null) break;
|
|
}
|
|
return curTag;
|
|
}
|
|
|
|
public static XmlTag findSubTagWithValue(XmlTag rootTag, String tagName, String tagValue) {
|
|
if (rootTag == null) return null;
|
|
final XmlTag[] subTags = rootTag.findSubTags(tagName);
|
|
for (int i = 0; i < subTags.length; i++) {
|
|
XmlTag subTag = subTags[i];
|
|
if (subTag.getValue().getTrimmedText().equals(tagValue)) {
|
|
return subTag;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static XmlTag findSubTagWithValueOnAnyLevel(XmlTag baseTag, String tagName, String tagValue) {
|
|
final String baseValue = baseTag.getValue().getTrimmedText();
|
|
if (baseValue != null && baseValue.equals(tagValue)) {
|
|
return baseTag;
|
|
}
|
|
else {
|
|
final XmlTag[] subTags = baseTag.getSubTags();
|
|
for (int i = 0; i < subTags.length; i++) {
|
|
XmlTag subTag = subTags[i];
|
|
|
|
// It will return the first entry of subtag in the tree
|
|
final XmlTag subTagFound = findSubTagWithValueOnAnyLevel(subTag, tagName, tagValue);
|
|
if (subTagFound != null) return subTagFound;
|
|
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Read the function name and parameter names to find out what this function does... :-)
|
|
public static XmlTag find(String subTag, String withValue, String forTag, XmlTag insideRoot) {
|
|
final XmlTag[] forTags = insideRoot.findSubTags(forTag);
|
|
for (int i = 0; i < forTags.length; i++) {
|
|
XmlTag tag = forTags[i];
|
|
final XmlTag[] allTags = tag.findSubTags(subTag);
|
|
for (int j = 0; j < allTags.length; j++) {
|
|
XmlTag curTag = allTags[j];
|
|
if (curTag.getName().equals(subTag) && curTag.getValue().getTrimmedText().equalsIgnoreCase(withValue)) {
|
|
return tag;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static boolean isInAntBuildFile(XmlFile file){
|
|
if(file == null) return false;
|
|
if (file.getCopyableUserData(XmlFile.ANT_BUILD_FILE) != null) {
|
|
return true;
|
|
}
|
|
XmlDocument document = file.getDocument();
|
|
if(document != null){
|
|
XmlTag rootTag = document.getRootTag();
|
|
if(rootTag != null){
|
|
return ANT_URI.equals(rootTag.getNamespace());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static boolean isAntTargetDefinition(XmlAttribute attr) {
|
|
if (!isInAntBuildFile((XmlFile)attr.getContainingFile())) return false;
|
|
final XmlTag tag = attr.getParent();
|
|
|
|
return tag.getName().equals("target") && attr.getName().equals("name");
|
|
}
|
|
|
|
public static boolean isAntPropertyDefinition(XmlAttribute attr) {
|
|
final XmlTag parentTag = attr.getParent();
|
|
if(parentTag != null){
|
|
final PsiMetaData data = parentTag.getMetaData();
|
|
if(data instanceof AntPropertyDeclaration){
|
|
if(data.getDeclaration() == attr.getValueElement()) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
public static String getDefaultNamespace(final XmlDocument document) {
|
|
final XmlFile file = XmlUtil.getContainingFile(document);
|
|
if (file != null && file.getCopyableUserData(XmlFile.ANT_BUILD_FILE) != null) {
|
|
return ANT_URI;
|
|
}
|
|
|
|
XmlTag tag = document.getRootTag();
|
|
if (tag == null) return EMPTY_NAMESPACE;
|
|
if ("project".equals(tag.getName()) && tag.getContext() instanceof XmlDocument) {
|
|
if (tag.getAttributeValue("default") != null) {
|
|
return ANT_URI;
|
|
}
|
|
}
|
|
|
|
String namespace = getDtdUri(document);
|
|
if (namespace != null) return EMPTY_NAMESPACE;
|
|
|
|
if ("taglib".equals(tag.getName())) {
|
|
return TAGLIB_1_2_URI;
|
|
}
|
|
|
|
if (file != null) {
|
|
final FileType fileType = file.getFileType();
|
|
|
|
if (fileType == StdFileTypes.HTML ||
|
|
fileType == StdFileTypes.XHTML ||
|
|
fileType == StdFileTypes.JSPX
|
|
) {
|
|
return XHTML_URI;
|
|
}
|
|
}
|
|
|
|
return EMPTY_NAMESPACE;
|
|
}
|
|
|
|
|
|
public static String getDtdUri(XmlDocument document) {
|
|
if (document.getProlog() != null) {
|
|
final XmlDoctype doctype = document.getProlog().getDoctype();
|
|
if (doctype != null) {
|
|
return doctype.getDtdUri();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static void computeTag(XmlTag tag, final Map<String,List<String>> tagsMap, final Map<String,List<MyAttributeInfo>> attributesMap) {
|
|
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 (int i = 0; i < attributes.length; i++) {
|
|
final XmlAttribute attribute = attributes[i];
|
|
list.add(new MyAttributeInfo(attribute.getName()));
|
|
}
|
|
}
|
|
else {
|
|
final XmlAttribute[] attributes = tag.getAttributes();
|
|
Collections.sort((List)list);
|
|
Arrays.sort(attributes, new Comparator() {
|
|
public int compare(Object o1, Object o2) {
|
|
return ((XmlAttribute)o1).getName().compareTo(((XmlAttribute)o2).getName());
|
|
}
|
|
});
|
|
|
|
final Iterator<MyAttributeInfo> iter = list.iterator();
|
|
int index = 0;
|
|
list = new ArrayList<MyAttributeInfo>();
|
|
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);
|
|
tag.processElements(new FilterElementProcessor(new ClassFilter(XmlTag.class)) {
|
|
public void add(PsiElement element) {
|
|
XmlTag tag = (XmlTag)element;
|
|
if (!tags.contains(tag.getName())) {
|
|
tags.add(tag.getName());
|
|
}
|
|
computeTag(tag, tagsMap, attributesMap);
|
|
}
|
|
}, tag);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
public static String generateDocumentDTD(XmlDocument doc) {
|
|
final StringBuffer buffer = new StringBuffer();
|
|
final Map<String,List<String>> tags = new com.intellij.util.containers.HashMap<String, List<String>>();
|
|
final Map<String,List<MyAttributeInfo>> attributes = new com.intellij.util.containers.HashMap<String, List<MyAttributeInfo>>();
|
|
computeTag(doc.getRootTag(), tags, attributes);
|
|
final Iterator<String> iter = tags.keySet().iterator();
|
|
while (iter.hasNext()) {
|
|
final String tagName = iter.next();
|
|
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.endsWith(CompletionUtil.DUMMY_IDENTIFIER.trim())) return "";
|
|
|
|
final StringBuffer buffer = new StringBuffer();
|
|
{
|
|
buffer.append("<!ELEMENT " + name + " ");
|
|
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 " + name);
|
|
final Iterator<MyAttributeInfo> iter = attributes.iterator();
|
|
while (iter.hasNext()) {
|
|
final MyAttributeInfo info = iter.next();
|
|
buffer.append("\n " + generateAttributeDTD(info));
|
|
}
|
|
buffer.append(">\n");
|
|
}
|
|
}
|
|
return buffer.toString();
|
|
}
|
|
|
|
private static String generateAttributeDTD(MyAttributeInfo info) {
|
|
if (info.myName.endsWith(CompletionUtil.DUMMY_IDENTIFIER.trim())) return "";
|
|
final StringBuffer buffer = new StringBuffer();
|
|
buffer.append(info.myName + " ");
|
|
//if ("id".equals(info.myName)) {
|
|
// buffer.append("ID");
|
|
//}
|
|
//else if ("ref".equals(info.myName)) {
|
|
// buffer.append("IDREF");
|
|
//} else {
|
|
buffer.append("CDATA");
|
|
//}
|
|
if (info.myRequired) {
|
|
buffer.append(" #REQUIRED");
|
|
}
|
|
else {
|
|
buffer.append(" #IMPLIED");
|
|
}
|
|
return buffer.toString();
|
|
}
|
|
|
|
public static String trimLeadingSpacesInMultilineTagValue(String tagValue) {
|
|
return tagValue == null ? null : tagValue.replaceAll("\n\\s*", "\n");
|
|
}
|
|
|
|
public static String findNamespaceByPrefix(final String prefix, XmlTag contextTag) {
|
|
final String s = contextTag.getNamespaceByPrefix(prefix);
|
|
if (s != null) return s;
|
|
return EMPTY_NAMESPACE;
|
|
}
|
|
|
|
public static final String findPrefixByQualifiedName(String name) {
|
|
final int prefixEnd = name.indexOf(':');
|
|
if (prefixEnd > 0) {
|
|
return name.substring(0, prefixEnd);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
public static final String findLocalNameByQualifiedName(String name) {
|
|
return name.substring(name.indexOf(':') + 1);
|
|
}
|
|
|
|
|
|
public static String escapeString(String str) {
|
|
if (str == null) return null;
|
|
StringBuffer buffer = null;
|
|
for (int i = 0; i < str.length(); i++) {
|
|
String entity;
|
|
char ch = str.charAt(i);
|
|
switch (ch) {
|
|
case '\"':
|
|
entity = """;
|
|
break;
|
|
case '<':
|
|
entity = "<";
|
|
break;
|
|
case '>':
|
|
entity = ">";
|
|
break;
|
|
case '&':
|
|
entity = "&";
|
|
break;
|
|
default :
|
|
entity = null;
|
|
break;
|
|
}
|
|
if (buffer == null) {
|
|
if (entity != null) {
|
|
// An entity occurred, so we'll have to use StringBuffer
|
|
// (allocate room for it plus a few more entities).
|
|
buffer = new StringBuffer(str.length() + 20);
|
|
// Copy previous skipped characters and fall through
|
|
// to pickup current character
|
|
buffer.append(str.substring(0, i));
|
|
buffer.append(entity);
|
|
}
|
|
}
|
|
else {
|
|
if (entity == null) {
|
|
buffer.append(ch);
|
|
}
|
|
else {
|
|
buffer.append(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there were any entities, return the escaped characters
|
|
// that we put in the StringBuffer. Otherwise, just return
|
|
// the unmodified input string.
|
|
return buffer == null ? str : buffer.toString();
|
|
}
|
|
|
|
public static XmlFile getContainingFile(PsiElement element) {
|
|
while (!(element instanceof XmlFile) && element != null) {
|
|
element = element.getContext();
|
|
}
|
|
return (XmlFile)element;
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|