| /* |
| * 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.XmlFileType; |
| import com.intellij.ide.structureView.StructureViewBuilder; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.UserDataCache; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.vfs.VirtualFileWithId; |
| import com.intellij.psi.PsiErrorElement; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.psi.impl.PsiFileEx; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.stubs.ObjectStubTree; |
| import com.intellij.psi.stubs.Stub; |
| import com.intellij.psi.stubs.StubTreeLoader; |
| 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.Function; |
| import com.intellij.util.indexing.FileBasedIndex; |
| import com.intellij.util.xml.*; |
| import com.intellij.util.xml.structure.DomStructureViewBuilder; |
| import com.intellij.util.xml.stubs.FileStub; |
| import com.intellij.xml.util.XmlUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| /** |
| * @author Gregory.Shrago |
| */ |
| public class DomServiceImpl extends DomService { |
| private static final Key<CachedValue<XmlFileHeader>> ROOT_TAG_NS_KEY = Key.create("rootTag&ns"); |
| private static final UserDataCache<CachedValue<XmlFileHeader>,XmlFile,Object> ourRootTagCache = new UserDataCache<CachedValue<XmlFileHeader>, XmlFile, Object>() { |
| @Override |
| protected CachedValue<XmlFileHeader> compute(final XmlFile file, final Object o) { |
| return CachedValuesManager.getManager(file.getProject()).createCachedValue(new CachedValueProvider<XmlFileHeader>() { |
| @Override |
| public Result<XmlFileHeader> compute() { |
| return new Result<XmlFileHeader>(calcXmlFileHeader(file), file); |
| } |
| }, false); |
| } |
| }; |
| |
| @NotNull |
| private static XmlFileHeader calcXmlFileHeader(final XmlFile file) { |
| |
| if (file instanceof PsiFileEx && ((PsiFileEx)file).isContentsLoaded() && file.getNode().isParsed()) { |
| return computeHeaderByPsi(file); |
| } |
| |
| if (!XmlUtil.isStubBuilding() && file.getFileType() == XmlFileType.INSTANCE) { |
| VirtualFile virtualFile = file.getVirtualFile(); |
| if (virtualFile instanceof VirtualFileWithId) { |
| ObjectStubTree tree = StubTreeLoader.getInstance().readFromVFile(file.getProject(), virtualFile); |
| if (tree != null) { |
| Stub root = tree.getRoot(); |
| if (root instanceof FileStub) { |
| return ((FileStub)root).getHeader(); |
| } |
| } |
| } |
| } |
| |
| if (!file.isValid()) return XmlFileHeader.EMPTY; |
| |
| return NanoXmlUtil.parseHeader(file); |
| } |
| |
| private static XmlFileHeader computeHeaderByPsi(XmlFile file) { |
| final XmlDocument document = file.getDocument(); |
| if (document == null) { |
| return XmlFileHeader.EMPTY; |
| } |
| |
| String publicId = null; |
| String systemId = null; |
| final XmlProlog prolog = document.getProlog(); |
| if (prolog != null) { |
| final XmlDoctype doctype = prolog.getDoctype(); |
| if (doctype != null) { |
| publicId = doctype.getPublicId(); |
| systemId = doctype.getSystemId(); |
| if (systemId == null) { |
| systemId = doctype.getDtdUri(); |
| } |
| } |
| } |
| |
| final XmlTag tag = document.getRootTag(); |
| if (tag == null) { |
| return XmlFileHeader.EMPTY; |
| } |
| |
| String localName = tag.getLocalName(); |
| if (StringUtil.isNotEmpty(localName)) { |
| if (tag.getPrevSibling() instanceof PsiErrorElement) { |
| return XmlFileHeader.EMPTY; |
| } |
| |
| String psiNs = tag.getNamespace(); |
| return new XmlFileHeader(localName, psiNs == XmlUtil.EMPTY_URI || Comparing.equal(psiNs, systemId) ? null : psiNs, publicId, |
| systemId); |
| } |
| return XmlFileHeader.EMPTY; |
| } |
| |
| @Override |
| public ModelMerger createModelMerger() { |
| return new ModelMergerImpl(); |
| } |
| |
| @Override |
| public <T extends DomElement> DomAnchor<T> createAnchor(T domElement) { |
| return DomAnchorImpl.createAnchor(domElement); |
| } |
| |
| @Override |
| @NotNull |
| public XmlFile getContainingFile(@NotNull DomElement domElement) { |
| if (domElement instanceof DomFileElement) { |
| return ((DomFileElement)domElement).getFile(); |
| } |
| return DomManagerImpl.getNotNullHandler(domElement).getFile(); |
| } |
| |
| @Override |
| @NotNull |
| public EvaluatedXmlName getEvaluatedXmlName(@NotNull final DomElement element) { |
| return DomManagerImpl.getNotNullHandler(element).getXmlName(); |
| } |
| |
| @Override |
| @NotNull |
| public XmlFileHeader getXmlFileHeader(XmlFile file) { |
| return file.isValid() ? ourRootTagCache.get(ROOT_TAG_NS_KEY, file, null).getValue() : XmlFileHeader.EMPTY; |
| } |
| |
| |
| @Override |
| public Collection<VirtualFile> getDomFileCandidates(Class<? extends DomElement> description, Project project) { |
| return FileBasedIndex.getInstance().getContainingFiles(DomFileIndex.NAME, description.getName(), GlobalSearchScope.allScope(project)); |
| } |
| |
| @Override |
| public <T extends DomElement> List<DomFileElement<T>> getFileElements(final Class<T> clazz, final Project project, @Nullable final GlobalSearchScope scope) { |
| final Collection<VirtualFile> list = scope == null ? getDomFileCandidates(clazz, project) : getDomFileCandidates(clazz, project, scope); |
| final ArrayList<DomFileElement<T>> result = new ArrayList<DomFileElement<T>>(list.size()); |
| for (VirtualFile file : list) { |
| final PsiFile psiFile = PsiManager.getInstance(project).findFile(file); |
| if (psiFile instanceof XmlFile) { |
| final DomFileElement<T> element = DomManager.getDomManager(project).getFileElement((XmlFile)psiFile, clazz); |
| if (element != null) { |
| result.add(element); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| |
| @Override |
| public StructureViewBuilder createSimpleStructureViewBuilder(final XmlFile file, final Function<DomElement, StructureViewMode> modeProvider) { |
| return new DomStructureViewBuilder(file, modeProvider); |
| } |
| |
| } |