blob: 6fc82840d587fe210bd05d7e9e42e2586a1c397f [file] [log] [blame]
/*
* Copyright 2000-2009 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.psi.impl.meta;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataCache;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.position.PatternFilter;
import com.intellij.psi.meta.MetaDataContributor;
import com.intellij.psi.meta.MetaDataRegistrar;
import com.intellij.psi.meta.PsiMetaData;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Created by IntelliJ IDEA.
* User: ik
* Date: 07.05.2003
* Time: 3:31:09
* To change this template use Options | File Templates.
*/
public class MetaRegistry extends MetaDataRegistrar {
private static final Logger LOG = Logger.getInstance(MetaRegistry.class);
private static final List<MyBinding> ourBindings = ContainerUtil.createLockFreeCopyOnWriteList();
private static volatile boolean ourContributorsLoaded = false;
private static final Key<CachedValue<PsiMetaData>> META_DATA_KEY = Key.create("META DATA KEY");
public static void bindDataToElement(final PsiElement element, final PsiMetaData data) {
CachedValue<PsiMetaData> value =
CachedValuesManager.getManager(element.getProject()).createCachedValue(new CachedValueProvider<PsiMetaData>() {
@Override
public Result<PsiMetaData> compute() {
data.init(element);
return new Result<PsiMetaData>(data, data.getDependences());
}
});
element.putUserData(META_DATA_KEY, value);
}
public static PsiMetaData getMeta(final PsiElement element) {
final PsiMetaData base = getMetaBase(element);
return base != null ? base : null;
}
private static final UserDataCache<CachedValue<PsiMetaData>, PsiElement, Object> ourCachedMetaCache =
new UserDataCache<CachedValue<PsiMetaData>, PsiElement, Object>() {
@Override
protected CachedValue<PsiMetaData> compute(final PsiElement element, Object p) {
return CachedValuesManager.getManager(element.getProject()).createCachedValue(new CachedValueProvider<PsiMetaData>() {
@Override
public Result<PsiMetaData> compute() {
ensureContributorsLoaded();
for (final MyBinding binding : ourBindings) {
if (binding.myFilter.isClassAcceptable(element.getClass()) && binding.myFilter.isAcceptable(element, element.getParent())) {
final PsiMetaData data;
try {
data = binding.myDataClass.newInstance();
}
catch (InstantiationException e) {
throw new RuntimeException("failed to instantiate " + binding.myDataClass, e);
}
catch (IllegalAccessException e) {
throw new RuntimeException("failed to instantiate " + binding.myDataClass, e);
}
data.init(element);
Object[] dependences = data.getDependences();
for (Object dependence : dependences) {
if (dependence == null) {
LOG.error(data + "(" + binding.myDataClass + ") provided null dependency");
}
}
return new Result<PsiMetaData>(data, ArrayUtil.append(dependences, element));
}
}
return new Result<PsiMetaData>(null, element);
}
}, false);
}
};
private static void ensureContributorsLoaded() {
if (!ourContributorsLoaded) {
synchronized (ourBindings) {
if (!ourContributorsLoaded) {
for (MetaDataContributor contributor : Extensions.getExtensions(MetaDataContributor.EP_NAME)) {
contributor.contributeMetaData(MetaDataRegistrar.getInstance());
}
ourContributorsLoaded = true;
}
}
}
}
@Nullable
public static PsiMetaData getMetaBase(final PsiElement element) {
ProgressIndicatorProvider.checkCanceled();
return ourCachedMetaCache.get(META_DATA_KEY, element, null).getValue();
}
/**
* @see com.intellij.psi.meta.MetaDataContributor
* @deprecated
*/
public static <T extends PsiMetaData> void addMetadataBinding(ElementFilter filter,
Class<T> aMetadataClass,
Disposable parentDisposable) {
final MyBinding binding = new MyBinding(filter, aMetadataClass);
addBinding(binding);
Disposer.register(parentDisposable, new Disposable() {
@Override
public void dispose() {
ourBindings.remove(binding);
}
});
}
/**
* @see com.intellij.psi.meta.MetaDataContributor
* @deprecated
*/
public static <T extends PsiMetaData> void addMetadataBinding(ElementFilter filter, Class<T> aMetadataClass) {
addBinding(new MyBinding(filter, aMetadataClass));
}
private static void addBinding(final MyBinding binding) {
ourBindings.add(0, binding);
}
@Override
public <T extends PsiMetaData> void registerMetaData(ElementFilter filter, Class<T> metadataDescriptorClass) {
addMetadataBinding(filter, metadataDescriptorClass);
}
@Override
public <T extends PsiMetaData> void registerMetaData(ElementPattern<?> pattern, Class<T> metadataDescriptorClass) {
addMetadataBinding(new PatternFilter(pattern), metadataDescriptorClass);
}
private static class MyBinding {
private final ElementFilter myFilter;
private final Class<? extends PsiMetaData> myDataClass;
public MyBinding(@NotNull ElementFilter filter, @NotNull Class<? extends PsiMetaData> dataClass) {
myFilter = filter;
myDataClass = dataClass;
}
}
}