blob: 46ce5b49a362e93c29b5200891b3463a52b579c0 [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.impl.dtd;
import com.intellij.javaee.ExternalResourceManager;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataCache;
import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.ClassFilter;
import com.intellij.psi.meta.PsiMetaData;
import com.intellij.psi.meta.PsiWritableMetaData;
import com.intellij.psi.scope.processor.FilterElementProcessor;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xml.XmlAttributeDescriptor;
import com.intellij.xml.XmlElementDescriptor;
import com.intellij.xml.XmlElementsGroup;
import com.intellij.xml.XmlNSDescriptor;
import com.intellij.xml.util.XmlNSDescriptorSequence;
import com.intellij.xml.util.XmlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
/**
* @author Mike
*/
public class XmlElementDescriptorImpl extends BaseXmlElementDescriptorImpl implements PsiWritableMetaData {
protected XmlElementDecl myElementDecl;
private String myName;
private static final Class[] ourParentClassesToScanAttributes = new Class[] { XmlMarkupDecl.class, XmlDocument.class };
private static final Key<CachedValue<XmlAttlistDecl[]>> ourCachedAttlistKeys = Key.create("cached_declarations");
public XmlElementDescriptorImpl(XmlElementDecl elementDecl) {
init(elementDecl);
}
public XmlElementDescriptorImpl() {
}
private static final UserDataCache<CachedValue<XmlAttlistDecl[]>,XmlElement, Object> myAttlistDeclCache = new UserDataCache<CachedValue<XmlAttlistDecl[]>,XmlElement, Object>() {
@Override
protected final CachedValue<XmlAttlistDecl[]> compute(final XmlElement owner, Object o) {
return CachedValuesManager.getManager(owner.getProject()).createCachedValue(new CachedValueProvider<XmlAttlistDecl[]>() {
@Override
public Result<XmlAttlistDecl[]> compute() {
return new Result<XmlAttlistDecl[]>(doCollectAttlistDeclarations(owner),owner);
}
});
}
};
@Override
public PsiElement getDeclaration(){
return myElementDecl;
}
@Override
public String getName(PsiElement context){
return getName();
}
@Override
public String getName() {
if (myName!=null) return myName;
return myName = myElementDecl.getName();
}
@Override
public void init(PsiElement element){
myElementDecl = (XmlElementDecl) element;
}
@Override
@SuppressWarnings("SpellCheckingInspection")
public Object[] getDependences(){
return new Object[]{myElementDecl, ExternalResourceManager.getInstance()};
}
@Override
public XmlNSDescriptor getNSDescriptor() {
return getNsDescriptorFrom(myElementDecl);
}
@Nullable
private static XmlNSDescriptor getNsDescriptorFrom(final PsiElement elementDecl) {
final XmlFile file = XmlUtil.getContainingFile(elementDecl);
if (file == null) {
return null;
}
final XmlDocument document = file.getDocument();
assert document != null;
XmlNSDescriptor descriptor = (XmlNSDescriptor)document.getMetaData();
return descriptor == null ? document.getDefaultNSDescriptor(XmlUtil.EMPTY_URI, false) : descriptor;
}
// Read-only action
@Override
protected final XmlElementDescriptor[] doCollectXmlDescriptors(final XmlTag context) {
final LinkedHashSet<XmlElementDescriptor> result = new LinkedHashSet<XmlElementDescriptor>();
final XmlElementContentSpec contentSpecElement = myElementDecl.getContentSpecElement();
final XmlNSDescriptor nsDescriptor = getNSDescriptor();
final XmlNSDescriptor NSDescriptor = nsDescriptor != null? nsDescriptor:getNsDescriptorFrom(context);
XmlUtil.processXmlElements(contentSpecElement, new PsiElementProcessor(){
@Override
public boolean execute(@NotNull PsiElement child){
if (child instanceof XmlToken) {
final XmlToken token = (XmlToken)child;
if (token.getTokenType() == XmlTokenType.XML_NAME) {
final String text = child.getText();
XmlElementDescriptor element = getElementDescriptor(text, NSDescriptor);
if (element != null) {
result.add(element);
}
}
else if (token.getTokenType() == XmlTokenType.XML_CONTENT_ANY) {
if (NSDescriptor instanceof XmlNSDescriptorImpl) {
ContainerUtil.addAll(result, ((XmlNSDescriptorImpl)NSDescriptor).getElements());
} else if (NSDescriptor instanceof XmlNSDescriptorSequence) {
for (XmlNSDescriptor xmlNSDescriptor : ((XmlNSDescriptorSequence)NSDescriptor).getSequence()) {
if (xmlNSDescriptor instanceof XmlNSDescriptorImpl) {
ContainerUtil.addAll(result, ((XmlNSDescriptorImpl)xmlNSDescriptor).getElements());
}
}
}
}
}
return true;
}
}, true, false, XmlUtil.getContainingFile(getDeclaration()));
return result.toArray(new XmlElementDescriptor[result.size()]);
}
private static XmlElementDescriptor getElementDescriptor(final String text, final XmlNSDescriptor NSDescriptor) {
XmlElementDescriptor element = null;
if (NSDescriptor instanceof XmlNSDescriptorImpl) {
element = ((XmlNSDescriptorImpl)NSDescriptor).getElementDescriptor(text);
}
else if (NSDescriptor instanceof XmlNSDescriptorSequence) {
final List<XmlNSDescriptor> sequence = ((XmlNSDescriptorSequence)NSDescriptor).getSequence();
for (XmlNSDescriptor xmlNSDescriptor : sequence) {
if (xmlNSDescriptor instanceof XmlNSDescriptorImpl) {
element = ((XmlNSDescriptorImpl)xmlNSDescriptor).getElementDescriptor(text);
if(element != null) break;
}
}
}
else {
element = null;
}
return element;
}
// Read-only calculation
@Override
protected final XmlAttributeDescriptor[] collectAttributeDescriptors(final XmlTag context) {
final List<XmlAttributeDescriptor> result = new SmartList<XmlAttributeDescriptor>();
for (XmlAttlistDecl attlistDecl : findAttlistDeclarations(getName())) {
for (XmlAttributeDecl attributeDecl : attlistDecl.getAttributeDecls()) {
final PsiMetaData psiMetaData = attributeDecl.getMetaData();
assert psiMetaData instanceof XmlAttributeDescriptor;
result.add((XmlAttributeDescriptor)psiMetaData);
}
}
return result.toArray(new XmlAttributeDescriptor[result.size()]);
}
// Read-only calculation
@Override
protected HashMap<String, XmlAttributeDescriptor> collectAttributeDescriptorsMap(final XmlTag context) {
final HashMap<String, XmlAttributeDescriptor> localADM;
final XmlAttributeDescriptor[] xmlAttributeDescriptors = getAttributesDescriptors(context);
localADM = new HashMap<String, XmlAttributeDescriptor>(xmlAttributeDescriptors.length);
for (final XmlAttributeDescriptor xmlAttributeDescriptor : xmlAttributeDescriptors) {
localADM.put(xmlAttributeDescriptor.getName(), xmlAttributeDescriptor);
}
return localADM;
}
private XmlAttlistDecl[] findAttlistDeclarations(String elementName) {
final List<XmlAttlistDecl> result = new ArrayList<XmlAttlistDecl>();
for (final XmlAttlistDecl declaration : getAttlistDeclarations()) {
final String name = declaration.getName();
if (name != null && name.equals(elementName)) {
result.add(declaration);
}
}
return result.toArray(new XmlAttlistDecl[result.size()]);
}
private XmlAttlistDecl[] getAttlistDeclarations() {
return getCachedAttributeDeclarations((XmlElement)getDeclaration());
}
public static @NotNull XmlAttlistDecl[] getCachedAttributeDeclarations(@Nullable XmlElement owner) {
if (owner == null) return XmlAttlistDecl.EMPTY_ARRAY;
owner = (XmlElement)PsiTreeUtil.getParentOfType(owner, ourParentClassesToScanAttributes);
if (owner == null) return XmlAttlistDecl.EMPTY_ARRAY;
return myAttlistDeclCache.get(ourCachedAttlistKeys, owner, null).getValue();
}
private static XmlAttlistDecl[] doCollectAttlistDeclarations(XmlElement xmlElement) {
final List<XmlAttlistDecl> result = new ArrayList<XmlAttlistDecl>();
XmlUtil.processXmlElements(xmlElement, new FilterElementProcessor(new ClassFilter(XmlAttlistDecl.class), result), false, false, XmlUtil.getContainingFile(xmlElement));
return result.toArray(new XmlAttlistDecl[result.size()]);
}
@Override
public XmlElementsGroup getTopGroup() {
XmlElementContentGroup topGroup = myElementDecl.getContentSpecElement().getTopGroup();
return topGroup == null ? null : new XmlElementsGroupImpl(topGroup, null);
}
@Override
public int getContentType() {
if (myElementDecl.getContentSpecElement().isAny()) {
return CONTENT_TYPE_ANY;
}
if (myElementDecl.getContentSpecElement().hasChildren()) {
return CONTENT_TYPE_CHILDREN;
}
if (myElementDecl.getContentSpecElement().isEmpty()) {
return CONTENT_TYPE_EMPTY;
}
if (myElementDecl.getContentSpecElement().isMixed()) {
return CONTENT_TYPE_MIXED;
}
return CONTENT_TYPE_ANY;
}
// Read-only calculation
@Override
protected HashMap<String, XmlElementDescriptor> collectElementDescriptorsMap(final XmlTag element) {
final HashMap<String, XmlElementDescriptor> elementDescriptorsMap;
final XmlElementDescriptor[] descriptors = getElementsDescriptors(element);
elementDescriptorsMap = new HashMap<String, XmlElementDescriptor>(descriptors.length);
for (final XmlElementDescriptor descriptor : descriptors) {
elementDescriptorsMap.put(descriptor.getName(), descriptor);
}
return elementDescriptorsMap;
}
@Override
public String getQualifiedName() {
return getName();
}
@Override
public String getDefaultName() {
return getName();
}
@Override
public void setName(final String name) throws IncorrectOperationException {
// IDEADEV-11439
myName = null;
}
}