dom: get rid of FileDescriptionCachedValueProvider, simplify DomFileElement creation

also fixes IDEA-215634 com.intellij.util.xml.DomFileDescription#isMyFile: parameter module is always null

GitOrigin-RevId: 19f4748847339f02d2799950ae8bc0a81f96834f
This commit is contained in:
peter
2019-06-12 14:01:35 +03:00
committed by intellij-monorepo-bot
parent c9ab759163
commit 058a1b1193
4 changed files with 87 additions and 245 deletions
@@ -15,17 +15,18 @@
*/
package com.intellij.util.xml.impl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.semantic.SemElement;
import com.intellij.util.ObjectUtils;
import com.intellij.util.xml.*;
import com.intellij.util.xml.reflect.*;
@@ -45,8 +46,7 @@ import java.util.Map;
/**
* @author peter
*/
public class DomFileElementImpl<T extends DomElement> implements DomFileElement<T> {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.impl.DomFileElementImpl");
public class DomFileElementImpl<T extends DomElement> implements DomFileElement<T>, SemElement {
private static final DomGenericInfo EMPTY_DOM_GENERIC_INFO = new DomGenericInfo() {
@Override
@@ -141,17 +141,13 @@ public class DomFileElementImpl<T extends DomElement> implements DomFileElement<
private final DomManagerImpl myManager;
private final Map<Key,Object> myUserData = new HashMap<>();
protected DomFileElementImpl(final XmlFile file,
final Class<T> rootElementClass,
final EvaluatedXmlNameImpl rootTagName,
final DomManagerImpl manager, final DomFileDescription<T> fileDescription,
FileStub stub) {
protected DomFileElementImpl(XmlFile file, EvaluatedXmlNameImpl rootTagName, DomFileDescription<T> fileDescription, FileStub stub) {
myFile = file;
myRootElementClass = rootElementClass;
myRootElementClass = fileDescription.getRootElementClass();
myRootTagName = rootTagName;
myManager = manager;
myManager = DomManagerImpl.getDomManager(file.getProject());
myFileDescription = fileDescription;
myRootHandler = new DomRootInvocationHandler(rootElementClass, new RootDomParentStrategy(this), this, rootTagName,
myRootHandler = new DomRootInvocationHandler(myRootElementClass, new RootDomParentStrategy(this), this, rootTagName,
stub == null ? null : stub.getRootTagStub());
}
@@ -304,18 +300,8 @@ public class DomFileElementImpl<T extends DomElement> implements DomFileElement<
@NotNull
public final T getRootElement() {
if (!isValid()) {
if (!myFile.isValid()) {
assert false: myFile + " is not valid";
} else {
final DomFileElementImpl<DomElement> fileElement = myManager.getFileElement(myFile);
if (fileElement == null) {
final FileDescriptionCachedValueProvider<DomElement> provider = myManager.getOrCreateCachedValueProvider(myFile);
String s = provider.getFileElementWithLogging();
LOG.error("Null, log=" + s);
} else {
assert false: this + " does not equal to " + fileElement;
}
}
PsiUtilCore.ensureValid(myFile);
throw new AssertionError(this + " is not equal to " + myManager.getFileElement(myFile));
}
return (T)getRootHandler().getProxy();
}
@@ -61,7 +61,7 @@ public final class DomManagerImpl extends DomManager {
static final Key<WeakReference<DomFileElementImpl>> CACHED_FILE_ELEMENT = Key.create("CACHED_FILE_ELEMENT");
static final Key<DomFileDescription> MOCK_DESCRIPTION = Key.create("MockDescription");
static final SemKey<FileDescriptionCachedValueProvider> FILE_DESCRIPTION_KEY = SemKey.createKey("FILE_DESCRIPTION_KEY");
static final SemKey<DomFileElementImpl> FILE_ELEMENT_KEY = SemKey.createKey("FILE_ELEMENT_KEY");
static final SemKey<DomInvocationHandler> DOM_HANDLER_KEY = SemKey.createKey("DOM_HANDLER_KEY");
static final SemKey<IndexedElementInvocationHandler> DOM_INDEXED_HANDLER_KEY = DOM_HANDLER_KEY.subKey("DOM_INDEXED_HANDLER_KEY");
static final SemKey<CollectionElementInvocationHandler> DOM_COLLECTION_HANDLER_KEY = DOM_HANDLER_KEY.subKey("DOM_COLLECTION_HANDLER_KEY");
@@ -271,14 +271,6 @@ public final class DomManagerImpl extends DomManager {
return fileElement;
}
@SuppressWarnings({"unchecked"})
@NotNull
final <T extends DomElement> FileDescriptionCachedValueProvider<T> getOrCreateCachedValueProvider(@NotNull XmlFile xmlFile) {
//noinspection ConstantConditions
return mySemService.getSemElement(FILE_DESCRIPTION_KEY, xmlFile);
}
public final Set<DomFileDescription> getFileDescriptions(String rootTagName) {
return myApplicationComponent.getFileDescriptions(rootTagName);
}
@@ -315,11 +307,9 @@ public final class DomManagerImpl extends DomManager {
@Override
@Nullable
public final <T extends DomElement> DomFileElementImpl<T> getFileElement(@Nullable XmlFile file) {
if (file == null) return null;
if (!(file.getFileType() instanceof DomSupportEnabled)) return null;
final VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile != null && virtualFile.isDirectory()) return null;
return this.<T>getOrCreateCachedValueProvider(file).getFileElement();
if (file == null || !(file.getFileType() instanceof DomSupportEnabled)) return null;
//noinspection unchecked
return mySemService.getSemElement(FILE_ELEMENT_KEY, file);
}
@Nullable
@@ -1,13 +1,22 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.xml.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.ide.highlighter.DomSupportEnabled;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectCoreUtil;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileWithId;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.PsiFileEx;
import com.intellij.psi.stubs.ObjectStubTree;
import com.intellij.psi.stubs.StubTreeLoader;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlElementType;
import com.intellij.psi.xml.XmlFile;
@@ -16,18 +25,20 @@ import com.intellij.semantic.SemContributor;
import com.intellij.semantic.SemRegistrar;
import com.intellij.semantic.SemService;
import com.intellij.util.ArrayUtil;
import com.intellij.util.xml.EvaluatedXmlName;
import com.intellij.util.xml.EvaluatedXmlNameImpl;
import com.intellij.util.xml.XmlName;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.xml.*;
import com.intellij.util.xml.reflect.CustomDomChildrenDescription;
import com.intellij.util.xml.reflect.DomChildrenDescription;
import com.intellij.util.xml.reflect.DomCollectionChildDescription;
import com.intellij.util.xml.reflect.DomFixedChildDescription;
import com.intellij.util.xml.stubs.DomStub;
import com.intellij.util.xml.stubs.ElementStub;
import com.intellij.util.xml.stubs.FileStub;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.WeakReference;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
@@ -42,18 +53,12 @@ import static com.intellij.patterns.XmlPatterns.*;
final class DomSemContributor extends SemContributor {
@Override
public void registerSemProviders(@NotNull SemRegistrar registrar, @NotNull Project project) {
registrar.registerSemElementProvider(DomManagerImpl.FILE_DESCRIPTION_KEY, xmlFile(), xmlFile -> {
ApplicationManager.getApplication().assertReadAccessAllowed();
return new FileDescriptionCachedValueProvider(DomManagerImpl.getDomManager(xmlFile.getProject()), xmlFile);
});
registrar.registerSemElementProvider(DomManagerImpl.FILE_ELEMENT_KEY, xmlFile(), DomSemContributor::createFileElement);
final SemService semService = SemService.getSemService(project);
registrar.registerSemElementProvider(DomManagerImpl.DOM_HANDLER_KEY, xmlTag().withParent(psiElement(XmlElementType.XML_DOCUMENT).withParent(xmlFile())),
xmlTag -> {
final FileDescriptionCachedValueProvider provider =
semService.getSemElement(DomManagerImpl.FILE_DESCRIPTION_KEY, xmlTag.getContainingFile());
assert provider != null;
final DomFileElementImpl element = provider.getFileElement();
DomFileElementImpl element = semService.getSemElement(DomManagerImpl.FILE_ELEMENT_KEY, xmlTag.getContainingFile());
if (element != null) {
final DomRootInvocationHandler handler = element.getRootHandler();
if (handler.getXmlTag() == xmlTag) {
@@ -169,6 +174,63 @@ final class DomSemContributor extends SemContributor {
DomSemContributor::createAttributeHandler);
}
@Nullable
private static DomFileElementImpl createFileElement(XmlFile xmlFile) {
VirtualFile file = xmlFile.getVirtualFile();
if (!(xmlFile.getFileType() instanceof DomSupportEnabled) || file != null && ProjectCoreUtil.isProjectOrWorkspaceFile(file)) {
return null;
}
DomFileDescription description = findFileDescription(xmlFile);
if (description == null) {
return null;
}
XmlName xmlName = DomImplUtil.createXmlName(description.getRootTagName(), description.getRootElementClass(), null);
assert xmlName != null;
EvaluatedXmlNameImpl rootTagName1 = EvaluatedXmlNameImpl.createEvaluatedXmlName(xmlName, xmlName.getNamespaceKey(), false);
FileStub stub = null;
DomFileMetaData meta = DomApplicationComponent.getInstance().findMeta(description);
if (meta != null && meta.hasStubs() && file instanceof VirtualFileWithId && !isFileParsed(xmlFile)) {
if (FileBasedIndex.getInstance().getFileBeingCurrentlyIndexed() == null) {
ObjectStubTree stubTree = StubTreeLoader.getInstance().readFromVFile(xmlFile.getProject(), file);
if (stubTree != null) {
stub = (FileStub)stubTree.getRoot();
}
}
}
//noinspection unchecked
DomFileElementImpl element = new DomFileElementImpl(xmlFile, rootTagName1, description, stub);
xmlFile.putUserData(DomManagerImpl.CACHED_FILE_ELEMENT, new WeakReference<>(element));
return element;
}
private static boolean isFileParsed(XmlFile myXmlFile) {
return myXmlFile instanceof PsiFileEx && ((PsiFileEx)myXmlFile).isContentsLoaded();
}
@Nullable
private static DomFileDescription<?> findFileDescription(XmlFile file) {
DomFileDescription mockDescription = file.getUserData(DomManagerImpl.MOCK_DESCRIPTION);
if (mockDescription != null) return mockDescription;
Project project = file.getProject();
XmlFile originalFile = (XmlFile)file.getOriginalFile();
if (!originalFile.equals(file)) {
DomFileElementImpl element = SemService.getSemService(project).getSemElement(DomManagerImpl.FILE_ELEMENT_KEY, originalFile);
return element == null ? null : element.getFileDescription();
}
DomManagerImpl domManager = DomManagerImpl.getDomManager(project);
Module module = ModuleUtilCore.findModuleForFile(file);
Condition<DomFileDescription> condition = d -> d.isMyFile(file, module);
String rootTagLocalName = DomService.getInstance().getXmlFileHeader(file).getRootTagLocalName();
DomFileDescription<?> description = ContainerUtil.find(domManager.getFileDescriptions(rootTagLocalName), condition);
return description != null ? description : ContainerUtil.find(domManager.getAcceptingOtherRootTagNameDescriptions(), condition);
}
@Nullable
static DomInvocationHandler getParentDom(@NotNull XmlTag tag) {
LinkedHashSet<XmlTag> allParents = new LinkedHashSet<>();
@@ -1,196 +0,0 @@
/*
* 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.impl;
import com.intellij.ide.highlighter.DomSupportEnabled;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.ProjectCoreUtil;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileWithId;
import com.intellij.psi.impl.PsiFileEx;
import com.intellij.psi.stubs.ObjectStubTree;
import com.intellij.psi.stubs.StubTreeLoader;
import com.intellij.psi.xml.XmlFile;
import com.intellij.semantic.SemElement;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.xml.*;
import com.intellij.util.xml.stubs.FileStub;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
/**
* @author peter
*/
@SuppressWarnings({"HardCodedStringLiteral", "StringConcatenationInsideStringBufferAppend"})
class FileDescriptionCachedValueProvider<T extends DomElement> implements SemElement{
private final XmlFile myXmlFile;
private volatile boolean myComputed;
private volatile DomFileElementImpl<T> myLastResult;
private final MyCondition myCondition = new MyCondition();
private final DomManagerImpl myDomManager;
private final DomService myDomService;
FileDescriptionCachedValueProvider(final DomManagerImpl domManager, final XmlFile xmlFile) {
myDomManager = domManager;
myXmlFile = xmlFile;
myDomService = DomService.getInstance();
}
@Nullable
public final DomFileElementImpl<T> getFileElement() {
if (myComputed) return myLastResult;
DomFileElementImpl<T> result = _computeFileElement(null);
synchronized (myCondition) {
if (myComputed) return myLastResult;
myLastResult = result;
WeakReference<DomFileElementImpl> ref = result != null ? new WeakReference<>(result) : null;
myXmlFile.putUserData(DomManagerImpl.CACHED_FILE_ELEMENT, ref);
myComputed = true;
return result;
}
}
@Nullable
private DomFileElementImpl<T> _computeFileElement(@Nullable StringBuilder sb) {
if (!myXmlFile.isValid()) {
return null;
}
if (sb != null) {
sb.append("File is valid\n");
}
VirtualFile file = myXmlFile.getVirtualFile();
if (!(myXmlFile.getFileType() instanceof DomSupportEnabled) || file != null && ProjectCoreUtil.isProjectOrWorkspaceFile(file)) {
return null;
}
XmlFileHeader rootTagName = myDomService.getXmlFileHeader(myXmlFile);
if (sb != null) {
sb.append(rootTagName).append(", file is of dom file type\n");
}
final DomFileDescription<T> description = findFileDescription(rootTagName, sb);
final DomFileElementImpl oldValue = getLastValue();
if (sb != null) {
sb.append("last " + oldValue + "\n");
}
if (description == null) {
return null;
}
final Class<T> rootElementClass = description.getRootElementClass();
final XmlName xmlName = DomImplUtil.createXmlName(description.getRootTagName(), rootElementClass, null);
assert xmlName != null;
final EvaluatedXmlNameImpl rootTagName1 = EvaluatedXmlNameImpl.createEvaluatedXmlName(xmlName, xmlName.getNamespaceKey(), false);
FileStub stub = null;
DomFileMetaData meta = DomApplicationComponent.getInstance().findMeta(description);
if (meta != null && meta.hasStubs() && file instanceof VirtualFileWithId && !isFileParsed()) {
ApplicationManager.getApplication().assertReadAccessAllowed();
if (FileBasedIndex.getInstance().getFileBeingCurrentlyIndexed() == null) {
ObjectStubTree stubTree = StubTreeLoader.getInstance().readFromVFile(myXmlFile.getProject(), file);
if (stubTree != null) {
stub = (FileStub)stubTree.getRoot();
}
}
}
DomFileElementImpl<T> result = new DomFileElementImpl<>(myXmlFile, rootElementClass, rootTagName1, myDomManager, description, stub);
if (sb != null) {
sb.append("success " + result + "\n");
}
return result;
}
private boolean isFileParsed() {
return myXmlFile instanceof PsiFileEx && ((PsiFileEx)myXmlFile).isContentsLoaded();
}
@Nullable
private DomFileDescription<T> findFileDescription(final XmlFileHeader xmlFileHeader, @Nullable StringBuilder sb) {
//noinspection unchecked
final DomFileDescription<T> mockDescription = myXmlFile.getUserData(DomManagerImpl.MOCK_DESCRIPTION);
if (mockDescription != null) return mockDescription;
if (sb != null) {
sb.append("no mock\n");
}
final XmlFile originalFile = (XmlFile)myXmlFile.getOriginalFile();
if (sb != null) {
sb.append("original: " + originalFile + "\n");
}
if (!originalFile.equals(myXmlFile)) {
final FileDescriptionCachedValueProvider<T> provider = myDomManager.getOrCreateCachedValueProvider(originalFile);
final DomFileElementImpl<T> element = provider.getFileElement();
if (sb != null) {
sb.append("originalDom " + element + "\n");
}
return element == null ? null : element.getFileDescription();
}
final Set<DomFileDescription> namedDescriptions = myDomManager.getFileDescriptions(xmlFileHeader.getRootTagLocalName());
if (sb != null) {
sb.append("named " + new HashSet<>(namedDescriptions) + "\n");
}
//noinspection unchecked
DomFileDescription<T> description = ContainerUtil.find(namedDescriptions, myCondition);
if (description == null) {
final Set<DomFileDescription> unnamed = myDomManager.getAcceptingOtherRootTagNameDescriptions();
//noinspection unchecked
description = ContainerUtil.find(unnamed, myCondition);
}
if (sb != null) {
sb.append("found " + description + "\n");
}
return description;
}
@Nullable
final DomFileElementImpl<T> getLastValue() {
return myLastResult;
}
public String getFileElementWithLogging() {
StringBuilder log = new StringBuilder();
myLastResult = _computeFileElement(log);
return log.toString();
}
private class MyCondition implements Condition<DomFileDescription> {
public Module module;
@Override
public boolean value(final DomFileDescription description) {
return description.isMyFile(myXmlFile, module);
}
}
}