blob: 65a91cc93a1607fcd42a0cb8810e590dfe33eed8 [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.schema;
import com.intellij.codeInsight.daemon.Validator;
import com.intellij.codeInsight.daemon.XmlErrorMessages;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import com.intellij.xml.XmlElementDescriptor;
import com.intellij.xml.util.XmlUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
/**
* @author Maxim.Mossienko
*/
public class SchemaNSDescriptor extends XmlNSDescriptorImpl {
@NonNls private static final String MIN_OCCURS_ATTR_NAME = "minOccurs";
@NonNls private static final String MAX_OCCURS_ATTR_VALUE = "maxOccurs";
@NonNls private static final String MAX_OCCURS_ATTR_NAME = MAX_OCCURS_ATTR_VALUE;
@NonNls private static final String ID_ATTR_NAME = "id";
@NonNls private static final String REF_ATTR_NAME = "ref";
@NonNls private static final String DEFAULT_ATTR_NAME = "default";
@NonNls private static final String FIXED_ATTR_NAME = "fixed";
@NonNls private static final String NAME_ATTR_NAME = "name";
private static final Validator<XmlTag> ELEMENT_VALIDATOR = new Validator<XmlTag>() {
@Override
public void validate(@NotNull final XmlTag tag, @NotNull ValidationHost host) {
if (!isFromSchemaNs(tag)) return;
final boolean hasRefAttribute = tag.getAttributeValue(REF_ATTR_NAME) != null;
if (hasRefAttribute) {
for(XmlAttribute attr:tag.getAttributes()) {
final String name = attr.getName();
if (name.indexOf(':') == -1 &&
!MIN_OCCURS_ATTR_NAME.equals(name) &&
!MAX_OCCURS_ATTR_NAME.equals(name) &&
!ID_ATTR_NAME.equals(name) &&
!REF_ATTR_NAME.equals(name)) {
host.addMessage(
attr.getNameElement(),
XmlErrorMessages.message("xml.schema.validation.attr.not.allowed.with.ref", name),
ValidationHost.ErrorType.ERROR
);
}
}
}
final String minOccursValue = tag.getAttributeValue("minOccurs");
final String maxOccursValue = tag.getAttributeValue(MAX_OCCURS_ATTR_VALUE);
if (minOccursValue != null && maxOccursValue != null) {
try {
final int minOccurs = Integer.parseInt(minOccursValue);
final int maxOccurs = Integer.parseInt(maxOccursValue);
if (maxOccurs < minOccurs) {
host.addMessage(
tag.getAttribute(MAX_OCCURS_ATTR_VALUE, null).getValueElement(),
XmlErrorMessages.message("xml.schema.validation.max.occurs.should.be.not.less.than.min.occurs"),
ValidationHost.ErrorType.ERROR
);
}
}
catch (NumberFormatException e) {
// this schema will be reported by xerces validation
}
}
if (!hasRefAttribute && tag.getAttributeValue(NAME_ATTR_NAME) == null) {
host.addMessage(
tag,
XmlErrorMessages.message("xml.schema.validation.name.or.ref.should.present"),
ValidationHost.ErrorType.ERROR
);
}
}
};
private static final Validator<XmlTag> ATTRIBUTE_VALIDATOR = new Validator<XmlTag>() {
@Override
public void validate(@NotNull final XmlTag tag, @NotNull ValidationHost host) {
if (!isFromSchemaNs(tag)) return;
if (tag.getAttributeValue(REF_ATTR_NAME) == null && tag.getAttributeValue(NAME_ATTR_NAME) == null) {
host.addMessage(
tag,
XmlErrorMessages.message("xml.schema.validation.name.or.ref.should.present"),
ValidationHost.ErrorType.ERROR
);
}
if (tag.getAttributeValue(DEFAULT_ATTR_NAME) != null && tag.getAttributeValue(FIXED_ATTR_NAME) != null) {
host.addMessage(
tag.getAttribute(DEFAULT_ATTR_NAME, null).getNameElement(),
XmlErrorMessages.message("xml.schema.validation.default.or.fixed.should.be.specified.but.not.both"),
ValidationHost.ErrorType.ERROR
);
host.addMessage(
tag.getAttribute(FIXED_ATTR_NAME, null).getNameElement(),
XmlErrorMessages.message("xml.schema.validation.default.or.fixed.should.be.specified.but.not.both"),
ValidationHost.ErrorType.ERROR
);
}
}
};
private static final XmlUtil.DuplicationInfoProvider<XmlTag> SCHEMA_ATTR_DUP_INFO_PROVIDER = new XmlUtil.DuplicationInfoProvider<XmlTag>() {
@Override
public String getName(@NotNull final XmlTag t) {
return t.getAttributeValue(NAME_ATTR_NAME);
}
@Override
@NotNull
public String getNameKey(@NotNull final XmlTag t, @NotNull String name) {
return name;
}
@Override
@NotNull
public PsiElement getNodeForMessage(@NotNull final XmlTag t) {
return t.getAttribute(NAME_ATTR_NAME, null).getValueElement();
}
};
private static final Validator<XmlTag> ELEMENT_AND_ATTR_VALIDATOR = new Validator<XmlTag>() {
@Override
public void validate(@NotNull final XmlTag tag, @NotNull ValidationHost host) {
if (!isFromSchemaNs(tag)) return;
final String nsPrefix = tag.getNamespacePrefix();
final XmlTag[] attrDeclTags = tag.findSubTags((nsPrefix.length() > 0 ? nsPrefix + ":" : "") + "attribute");
XmlUtil.doDuplicationCheckForElements(
attrDeclTags,
new HashMap<String, XmlTag>(attrDeclTags.length),
SCHEMA_ATTR_DUP_INFO_PROVIDER,
host
);
final XmlTag[] elementDeclTags = tag.findSubTags((nsPrefix.length() > 0 ? nsPrefix + ":" : "") + "element");
XmlUtil.doDuplicationCheckForElements(
elementDeclTags,
new HashMap<String, XmlTag>(elementDeclTags.length),
SCHEMA_ATTR_DUP_INFO_PROVIDER,
host
);
}
};
private static boolean isFromSchemaNs(final XmlTag tag) {
return XmlUtil.XML_SCHEMA_URI.equals(tag.getNamespace());
}
@Override
protected XmlElementDescriptor createElementDescriptor(final XmlTag tag) {
final XmlElementDescriptor descriptor = super.createElementDescriptor(tag);
String localName = tag.getAttributeValue(NAME_ATTR_NAME);
if (ELEMENT_TAG_NAME.equals(localName)) {
((XmlElementDescriptorImpl)descriptor).setValidator(ELEMENT_VALIDATOR);
} else if (COMPLEX_TYPE_TAG_NAME.equals(localName) || SCHEMA_TAG_NAME.equals(localName) || SEQUENCE_TAG_NAME.equals(localName)) {
((XmlElementDescriptorImpl)descriptor).setValidator(ELEMENT_AND_ATTR_VALIDATOR);
} else if (ATTRIBUTE_TAG_NAME.equals(localName)) {
((XmlElementDescriptorImpl)descriptor).setValidator(ATTRIBUTE_VALIDATOR);
}
return descriptor;
}
@Override
public String toString() {
return getDefaultNamespace();
}
}