blob: a0caa3453840029307301ac6427cc269c842ec51 [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.util.xml;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.xml.*;
import com.intellij.util.containers.ConcurrentFactoryMap;
import com.intellij.util.containers.ConcurrentHashMap;
import com.intellij.util.containers.FactoryMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* @author peter
*/
public class EvaluatedXmlNameImpl implements EvaluatedXmlName {
private static final Key<CachedValue<FactoryMap<String,List<String>>>> NAMESPACE_PROVIDER_KEY = Key.create("NamespaceProvider");
private static final Map<EvaluatedXmlNameImpl,EvaluatedXmlNameImpl> ourInterned = new ConcurrentHashMap<EvaluatedXmlNameImpl,EvaluatedXmlNameImpl>();
private final XmlName myXmlName;
private final String myNamespaceKey;
private final boolean myEqualToParent;
private EvaluatedXmlNameImpl(@NotNull final XmlName xmlName, @Nullable final String namespaceKey, final boolean equalToParent) {
myXmlName = xmlName;
myNamespaceKey = namespaceKey;
myEqualToParent = equalToParent;
}
@NotNull
public final String getLocalName() {
return myXmlName.getLocalName();
}
@Override
public final XmlName getXmlName() {
return myXmlName;
}
@Override
public final EvaluatedXmlName evaluateChildName(@NotNull final XmlName name) {
String namespaceKey = name.getNamespaceKey();
final boolean equalToParent = Comparing.equal(namespaceKey, myNamespaceKey);
if (namespaceKey == null) {
namespaceKey = myNamespaceKey;
}
return createEvaluatedXmlName(name, namespaceKey, equalToParent);
}
public String toString() {
return (myNamespaceKey == null ? "" : myNamespaceKey + " : ") + myXmlName.getLocalName();
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || !(o instanceof EvaluatedXmlNameImpl)) return false;
final EvaluatedXmlNameImpl that = (EvaluatedXmlNameImpl)o;
if (myEqualToParent != that.myEqualToParent) return false;
if (myNamespaceKey != null ? !myNamespaceKey.equals(that.myNamespaceKey) : that.myNamespaceKey != null) return false;
if (!myXmlName.equals(that.myXmlName)) return false;
return true;
}
@Override
public int hashCode() {
int result = myXmlName.hashCode();
result = 31 * result + (myNamespaceKey != null ? myNamespaceKey.hashCode() : 0);
result = 31 * result + (myEqualToParent ? 1 : 0);
return result;
}
public final boolean isNamespaceAllowed(DomFileElement element, String namespace) {
if (myNamespaceKey == null || myEqualToParent) return true;
final XmlFile file = element.getFile();
return isNamespaceAllowed(namespace, getAllowedNamespaces(file));
}
@NotNull
private List<String> getAllowedNamespaces(final XmlFile file) {
CachedValue<FactoryMap<String, List<String>>> value = file.getUserData(NAMESPACE_PROVIDER_KEY);
if (value == null) {
file.putUserData(NAMESPACE_PROVIDER_KEY, value = CachedValuesManager.getManager(file.getProject()).createCachedValue(new CachedValueProvider<FactoryMap<String, List<String>>>() {
@Override
public Result<FactoryMap<String, List<String>>> compute() {
final FactoryMap<String, List<String>> map = new ConcurrentFactoryMap<String, List<String>>() {
@Override
protected List<String> create(final String key) {
final DomFileDescription<?> description = DomManager.getDomManager(file.getProject()).getDomFileDescription(file);
if (description == null) return Collections.emptyList();
return description.getAllowedNamespaces(key, file);
}
};
return Result.create(map, file);
}
}, false));
}
final List<String> list = value.getValue().get(myNamespaceKey);
assert list != null;
return list;
}
private static boolean isNamespaceAllowed(final String namespace, final List<String> list) {
return list.contains(namespace) || StringUtil.isEmpty(namespace) && list.isEmpty();
}
@Override
public final boolean isNamespaceAllowed(String namespace, final XmlFile file, boolean qualified) {
return myNamespaceKey == null || myEqualToParent && !qualified || isNamespaceAllowed(namespace, getNamespaceList(file));
}
@Override
@NotNull @NonNls
public final String getNamespace(@NotNull XmlElement parentElement, final XmlFile file) {
final String xmlElementNamespace = getXmlElementNamespace(parentElement);
if (myNamespaceKey != null && !myEqualToParent) {
final List<String> strings = getAllowedNamespaces(file);
if (!strings.isEmpty() && !strings.contains(xmlElementNamespace)) {
return strings.get(0);
}
}
return xmlElementNamespace;
}
private static String getXmlElementNamespace(final XmlElement parentElement) {
if (parentElement instanceof XmlTag) {
return ((XmlTag)parentElement).getNamespace();
}
if (parentElement instanceof XmlAttribute) {
return ((XmlAttribute)parentElement).getNamespace();
}
if (parentElement instanceof XmlFile) {
final XmlDocument document = ((XmlFile)parentElement).getDocument();
if (document != null) {
final XmlTag tag = document.getRootTag();
if (tag != null) {
return tag.getNamespace();
}
}
return "";
}
throw new AssertionError("Can't get namespace of " + parentElement);
}
private List<String> getNamespaceList(final XmlFile file) {
return getAllowedNamespaces(file);
}
public static EvaluatedXmlNameImpl createEvaluatedXmlName(@NotNull final XmlName xmlName, @Nullable final String namespaceKey, boolean equalToParent) {
final EvaluatedXmlNameImpl name = new EvaluatedXmlNameImpl(xmlName, namespaceKey, equalToParent);
final EvaluatedXmlNameImpl interned = ourInterned.get(name);
if (interned != null) {
return interned;
}
ourInterned.put(name, name);
return name;
}
}