2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.xml
.util
;
18 import com
.intellij
.javaee
.ExternalResourceManagerEx
;
19 import com
.intellij
.javaee
.ExternalResourceManager
;
20 import com
.intellij
.javaee
.UriUtil
;
21 import com
.intellij
.openapi
.application
.ApplicationManager
;
22 import com
.intellij
.openapi
.diagnostic
.Logger
;
23 import com
.intellij
.openapi
.project
.Project
;
24 import com
.intellij
.openapi
.util
.text
.StringUtil
;
25 import com
.intellij
.openapi
.vfs
.VfsUtil
;
26 import com
.intellij
.openapi
.vfs
.VirtualFile
;
27 import com
.intellij
.openapi
.vfs
.VirtualFileManager
;
28 import com
.intellij
.openapi
.vfs
.ex
.http
.HttpFileSystem
;
29 import com
.intellij
.psi
.PsiFile
;
30 import com
.intellij
.psi
.PsiManager
;
31 import com
.intellij
.psi
.impl
.source
.resolve
.reference
.impl
.providers
.URIReferenceProvider
;
32 import com
.intellij
.psi
.xml
.XmlFile
;
33 import com
.intellij
.psi
.xml
.XmlDocument
;
34 import com
.intellij
.psi
.xml
.XmlTag
;
35 import com
.intellij
.xml
.actions
.ValidateXmlActionHandler
;
36 import com
.intellij
.xml
.XmlBundle
;
37 import org
.apache
.xerces
.xni
.XMLResourceIdentifier
;
38 import org
.apache
.xerces
.xni
.XNIException
;
39 import org
.apache
.xerces
.xni
.parser
.XMLEntityResolver
;
40 import org
.apache
.xerces
.xni
.parser
.XMLInputSource
;
41 import org
.jetbrains
.annotations
.NonNls
;
42 import org
.jetbrains
.annotations
.Nullable
;
43 import org
.xml
.sax
.SAXParseException
;
46 import java
.io
.IOException
;
47 import java
.io
.StringReader
;
48 import java
.net
.MalformedURLException
;
50 import java
.util
.HashMap
;
54 * Created by IntelliJ IDEA.
55 * User: Maxim.Mossienko
58 * To change this template use File | Settings | File Templates.
60 public class XmlResourceResolver
implements XMLEntityResolver
{
61 private static final Logger LOG
= Logger
.getInstance("#com.intellij.xml.util.XmlResourceResolver");
62 private final XmlFile myFile
;
63 private final Project myProject
;
64 private final Map
<String
,String
> myExternalResourcesMap
= new HashMap
<String
, String
>(1);
65 private boolean myStopOnUnDeclaredResource
;
67 public static final String FILE_PREFIX
= "file://";
68 private final ValidateXmlActionHandler
.ErrorReporter myErrorReporter
;
70 public XmlResourceResolver(XmlFile _xmlFile
, Project _project
, final ValidateXmlActionHandler
.ErrorReporter errorReporter
) {
73 myErrorReporter
= errorReporter
;
76 public String
getPathByPublicId(String baseId
) {
77 return myExternalResourcesMap
.get(baseId
);
80 public String
[] getResourcePaths() {
81 return myExternalResourcesMap
.values().toArray(new String
[myExternalResourcesMap
.size()]);
85 public PsiFile
resolve(final String baseSystemId
, final String _systemId
) {
86 if (LOG
.isDebugEnabled()) {
87 LOG
.debug("enter: resolveEntity(baseSystemId='" + baseSystemId
+ "' systemId='" + _systemId
+ "," + super.toString() + "')");
90 if (_systemId
== null) return null;
91 if (myStopOnUnDeclaredResource
&&
92 ExternalResourceManagerEx
.getInstanceEx().isIgnoredResource(_systemId
)) {
93 throw new IgnoredResourceException();
96 final int length
= URIReferenceProvider
.getPrefixLength(_systemId
);
97 final String systemId
= _systemId
.substring(length
);
99 final PsiFile
[] result
= new PsiFile
[] { null };
100 final Runnable action
= new Runnable() {
102 PsiFile baseFile
= null;
103 VirtualFile vFile
= null;
105 if (baseSystemId
!= null) {
106 baseFile
= resolve(null,baseSystemId
);
108 if (baseFile
== null) {
109 if (myFile
!= null) {
110 // Find relative to myFile
111 File workingFile
= new File("");
112 String workingDir
= workingFile
.getAbsoluteFile().getAbsolutePath().replace(File
.separatorChar
, '/');
113 String id
= StringUtil
.replace(baseSystemId
, workingDir
, myFile
.getVirtualFile().getParent().getPath());
114 vFile
= UriUtil
.findRelativeFile(id
, (VirtualFile
)null);
118 vFile
= UriUtil
.findRelativeFile(baseSystemId
, (VirtualFile
)null);
122 vFile
= VfsUtil
.findFileByURL(new URL(baseSystemId
));
123 } catch(MalformedURLException ex
) {}
128 if (vFile
!= null && !vFile
.isDirectory() && !(vFile
.getFileSystem() instanceof HttpFileSystem
)) {
129 baseFile
= PsiManager
.getInstance(myProject
).findFile(vFile
);
132 if (baseFile
== null) {
136 String version
= null;
137 if (baseFile
== myFile
) {
138 final XmlDocument document
= myFile
.getDocument();
139 final XmlTag rootTag
= document
!= null ? document
.getRootTag():null;
140 version
= rootTag
!= null ? rootTag
.getAttributeValue("version"):null;
143 PsiFile psiFile
= ExternalResourceManager
.getInstance().getResourceLocation(systemId
, baseFile
, version
);
144 if (psiFile
== null) {
145 psiFile
= XmlUtil
.findXmlFile(baseFile
, systemId
);
148 if (psiFile
== null && baseSystemId
!= null) {
149 String fullUrl
= baseSystemId
.substring( 0, baseSystemId
.lastIndexOf('/') + 1 ) + systemId
;
150 psiFile
= XmlUtil
.findXmlFile(baseFile
,fullUrl
);
153 if (LOG
.isDebugEnabled()) {
154 LOG
.debug("before relative file checking:"+psiFile
+","+systemId
+","+ baseSystemId
+")");
156 if (psiFile
== null && baseSystemId
== null) { // entity file
157 File workingFile
= new File("");
158 String workingDir
= workingFile
.getAbsoluteFile().getAbsolutePath().replace(File
.separatorChar
, '/') + "/";
160 String relativePath
= StringUtil
.replace(
166 if (relativePath
.equals(systemId
)) {
167 // on Windows systemId consisting of idea install path could become encoded DOS short name (e.g. idea%7f1.504)
168 // I am not aware how to get such name from 'workingDir' so let just pickup filename from there
169 relativePath
= systemId
.substring(systemId
.lastIndexOf('/') + 1);
172 if (LOG
.isDebugEnabled()) {
173 LOG
.debug("next to relative file checking:"+relativePath
+","+myExternalResourcesMap
.size()+")");
176 for(String path
:myExternalResourcesMap
.values()) {
177 if (LOG
.isDebugEnabled()) {
178 LOG
.debug("Finding file by url:" + path
);
180 VirtualFile file
= VirtualFileManager
.getInstance().findFileByUrl(path
);
181 if (file
== null) continue;
182 if (LOG
.isDebugEnabled()) {
183 LOG
.debug("Finding "+relativePath
+" relative to:"+file
.getPath());
185 final VirtualFile relativeFile
= UriUtil
.findRelativeFile(relativePath
, file
);
186 if (LOG
.isDebugEnabled()) {
187 LOG
.debug("Found "+(relativeFile
!= null ? relativeFile
.getPath():"null"));
190 if (relativeFile
!= null) {
191 psiFile
= PsiManager
.getInstance(myProject
).findFile(relativeFile
);
192 if (psiFile
!= null) break;
197 if (LOG
.isDebugEnabled()) {
198 LOG
.debug("resolveEntity: psiFile='" + (psiFile
!= null ? psiFile
.getVirtualFile() : null) + "'");
203 ApplicationManager
.getApplication().runReadAction(action
);
205 final PsiFile psiFile
= result
[0];
206 if (psiFile
!= null) {
207 final String url
= psiFile
.getVirtualFile().getUrl();
208 if (LOG
.isDebugEnabled()) {
209 LOG
.debug("Adding external resource ref:"+systemId
+","+url
+","+super.toString());
211 myExternalResourcesMap
.put(systemId
,url
);
217 public XMLInputSource
resolveEntity(XMLResourceIdentifier xmlResourceIdentifier
) throws XNIException
, IOException
{
218 String publicId
= xmlResourceIdentifier
.getLiteralSystemId() != null ?
219 xmlResourceIdentifier
.getLiteralSystemId():
220 xmlResourceIdentifier
.getNamespace();
222 PsiFile psiFile
= resolve(xmlResourceIdentifier
.getBaseSystemId(), publicId
);
223 if (psiFile
== null && xmlResourceIdentifier
.getBaseSystemId() != null) {
224 psiFile
= ExternalResourceManager
.getInstance().getResourceLocation(xmlResourceIdentifier
.getBaseSystemId(), myFile
, null);
226 if (psiFile
==null && xmlResourceIdentifier
.getLiteralSystemId()!=null && xmlResourceIdentifier
.getNamespace()!=null) {
228 xmlResourceIdentifier
.getBaseSystemId(),
229 publicId
= xmlResourceIdentifier
.getNamespace()
233 if (psiFile
== null) {
234 if (publicId
!= null && publicId
.indexOf(":/") != -1) {
235 myErrorReporter
.processError(
236 new SAXParseException(XmlBundle
.message("xml.validate.external.resource.is.not.registered", publicId
), publicId
, null, 0,0), false);
237 final XMLInputSource source
= new XMLInputSource(xmlResourceIdentifier
);
238 source
.setPublicId(publicId
);
239 source
.setCharacterStream(new StringReader(""));
245 XMLInputSource source
= new XMLInputSource(xmlResourceIdentifier
);
246 if (xmlResourceIdentifier
.getLiteralSystemId() == null) {
247 VirtualFile virtualFile
= psiFile
.getVirtualFile();
248 final String url
= VfsUtil
.fixIDEAUrl(virtualFile
.getUrl());
249 source
.setBaseSystemId(url
);
250 source
.setSystemId(url
);
252 source
.setPublicId(publicId
);
253 source
.setCharacterStream(new StringReader(psiFile
.getText()));
258 public void setStopOnUnDeclaredResource(final boolean stopOnUnDeclaredResource
) {
259 myStopOnUnDeclaredResource
= stopOnUnDeclaredResource
;
262 public static class IgnoredResourceException
extends RuntimeException
{