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
.javaee
;
18 import com
.intellij
.application
.options
.PathMacrosImpl
;
19 import com
.intellij
.application
.options
.ReplacePathToMacroMap
;
20 import com
.intellij
.openapi
.components
.ExpandMacroToPathMap
;
21 import com
.intellij
.openapi
.components
.ServiceManager
;
22 import com
.intellij
.openapi
.components
.State
;
23 import com
.intellij
.openapi
.components
.Storage
;
24 import com
.intellij
.openapi
.diagnostic
.Logger
;
25 import com
.intellij
.openapi
.extensions
.Extensions
;
26 import com
.intellij
.openapi
.fileTypes
.FileType
;
27 import com
.intellij
.openapi
.project
.Project
;
28 import com
.intellij
.openapi
.util
.*;
29 import com
.intellij
.openapi
.vfs
.VirtualFile
;
30 import com
.intellij
.psi
.PsiFile
;
31 import com
.intellij
.psi
.xml
.XmlFile
;
32 import com
.intellij
.util
.ArrayUtil
;
33 import com
.intellij
.util
.NullableFunction
;
34 import com
.intellij
.util
.containers
.HashMap
;
35 import com
.intellij
.xml
.XmlNSDescriptor
;
36 import com
.intellij
.xml
.XmlSchemaProvider
;
37 import com
.intellij
.xml
.util
.XmlUtil
;
38 import gnu
.trove
.THashSet
;
39 import org
.jdom
.Element
;
40 import org
.jetbrains
.annotations
.NonNls
;
41 import org
.jetbrains
.annotations
.NotNull
;
42 import org
.jetbrains
.annotations
.Nullable
;
50 @State(name
= "ExternalResourceManagerImpl",
51 storages
= {@Storage(id
= "other", file
= "$APP_CONFIG$/other.xml")})
52 public class ExternalResourceManagerImpl
extends ExternalResourceManagerEx
implements JDOMExternalizable
{
53 static final Logger LOG
= Logger
.getInstance("#com.intellij.j2ee.openapi.impl.ExternalResourceManagerImpl");
55 @NonNls public static final String J2EE_1_3
= "http://java.sun.com/dtd/";
56 @NonNls public static final String J2EE_1_2
= "http://java.sun.com/j2ee/dtds/";
57 @NonNls public static final String J2EE_NS
= "http://java.sun.com/xml/ns/j2ee/";
58 @NonNls public static final String JAVAEE_NS
= "http://java.sun.com/xml/ns/javaee/";
61 private final Map
<String
, Map
<String
, String
>> myResources
= new HashMap
<String
, Map
<String
, String
>>();
62 private final Set
<String
> myResourceLocations
= new HashSet
<String
>();
64 private final Set
<NullableFunction
<String
, XmlNSDescriptor
>> myImplicitNamespaces
=
65 new THashSet
<NullableFunction
<String
, XmlNSDescriptor
>>();
67 private final Set
<String
> myIgnoredResources
= new HashSet
<String
>();
69 private final AtomicNotNullLazyValue
<Map
<String
, Map
<String
, String
>>> myStdResources
= new AtomicNotNullLazyValue
<Map
<String
, Map
<String
, String
>>>() {
73 protected Map
<String
, Map
<String
, String
>> compute() {
74 return computeStdResources();
78 protected Map
<String
, Map
<String
, String
>> computeStdResources() {
79 ResourceRegistrarImpl registrar
= new ResourceRegistrarImpl();
80 for (StandardResourceProvider provider
: Extensions
.getExtensions(StandardResourceProvider
.EP_NAME
)) {
81 provider
.registerResources(registrar
);
83 myIgnoredResources
.addAll(registrar
.getIgnored());
84 return registrar
.getResources();
87 private final List
<ExternalResourceListener
> myListeners
= new ArrayList
<ExternalResourceListener
>();
88 private long myModificationCount
= 0;
89 private final PathMacrosImpl myPathMacros
;
90 @NonNls private static final String RESOURCE_ELEMENT
= "resource";
91 @NonNls private static final String URL_ATTR
= "url";
92 @NonNls private static final String LOCATION_ATTR
= "location";
93 @NonNls private static final String IGNORED_RESOURCE_ELEMENT
= "ignored-resource";
94 private static final String DEFAULT_VERSION
= null;
95 @NonNls public static final String STANDARD_SCHEMAS
= "/standardSchemas/";
97 public ExternalResourceManagerImpl(PathMacrosImpl pathMacros
) {
98 myPathMacros
= pathMacros
;
101 public static boolean isStandardResource(VirtualFile file
) {
102 VirtualFile parent
= file
.getParent();
103 return parent
!= null && parent
.getName().equals("standardSchemas");
106 public boolean isUserResource(VirtualFile file
) {
107 return myResourceLocations
.contains(file
.getUrl());
111 static Map
<String
, String
> getMap(@NotNull final Map
<String
, Map
<String
, String
>> resources
,
112 @Nullable final String version
,
113 final boolean create
) {
114 Map
<String
, String
> map
= resources
.get(version
);
117 map
= new HashMap
<String
, String
>();
118 resources
.put(version
, map
);
120 else if (version
== null || !version
.equals(DEFAULT_VERSION
)) {
121 map
= resources
.get(DEFAULT_VERSION
);
128 public String
getResourceLocation(String url
) {
129 return getResourceLocation(url
, DEFAULT_VERSION
);
132 public String
getResourceLocation(@NonNls String url
, String version
) {
133 Map
<String
, String
> map
= getMap(myResources
, version
, false);
134 String result
= map
!= null ? map
.get(url
) : null;
136 if (result
== null) {
137 map
= getMap(myStdResources
.getValue(), version
, false);
138 result
= map
!= null ? map
.get(url
) : null;
141 if (result
== null) {
148 public String
getResourceLocation(@NonNls String url
, @NotNull Project project
) {
149 String location
= getProjectResources(project
).getResourceLocation(url
);
150 return location
== null || location
.equals(url
) ?
getResourceLocation(url
) : location
;
153 public String
getResourceLocation(@NonNls String url
, String version
, @NotNull Project project
) {
154 String location
= getProjectResources(project
).getResourceLocation(url
, version
);
155 return location
== null || location
.equals(url
) ?
getResourceLocation(url
, version
) : location
;
159 public PsiFile
getResourceLocation(@NotNull @NonNls final String url
, @NotNull final PsiFile baseFile
, final String version
) {
160 final XmlFile schema
= XmlSchemaProvider
.findSchema(url
, baseFile
);
161 if (schema
!= null) {
164 final String location
= getResourceLocation(url
, version
, baseFile
.getProject());
165 return XmlUtil
.findXmlFile(baseFile
, location
);
168 public String
[] getResourceUrls(FileType fileType
, final boolean includeStandard
) {
169 return getResourceUrls(fileType
, DEFAULT_VERSION
, includeStandard
);
172 public String
[] getResourceUrls(@Nullable final FileType fileType
, @NonNls final String version
, final boolean includeStandard
) {
173 final List
<String
> result
= new LinkedList
<String
>();
174 addResourcesFromMap(fileType
, result
, version
, myResources
);
176 if (includeStandard
) {
177 addResourcesFromMap(fileType
, result
, version
, myStdResources
.getValue());
180 return ArrayUtil
.toStringArray(result
);
183 private static void addResourcesFromMap(@Nullable final FileType fileType
,
184 final List
<String
> result
,
186 Map
<String
, Map
<String
, String
>> resourcesMap
) {
187 Map
<String
, String
> resources
= getMap(resourcesMap
, version
, false);
188 if (resources
== null) return;
189 final Set
<String
> keySet
= resources
.keySet();
191 for (String key
: keySet
) {
192 String resource
= resources
.get(key
);
194 if (fileType
!= null) {
195 String defaultExtension
= fileType
.getDefaultExtension();
197 if (resource
.endsWith(defaultExtension
) &&
198 resource
.length() > defaultExtension
.length() &&
199 resource
.charAt(resource
.length() - defaultExtension
.length() - 1) == '.') {
209 public void addResource(String url
, String location
) {
210 addResource(url
, DEFAULT_VERSION
, location
);
213 public void addResource(@NonNls String url
, @NonNls String version
, @NonNls String location
) {
214 addSilently(url
, version
, location
);
215 fireExternalResourceChanged();
218 private void addSilently(String url
, String version
, String location
) {
219 final Map
<String
, String
> map
= getMap(myResources
, version
, true);
221 map
.put(url
, location
);
222 myResourceLocations
.add(location
);
223 myModificationCount
++;
226 public void removeResource(String url
) {
227 removeResource(url
, DEFAULT_VERSION
);
230 public void removeResource(String url
, String version
) {
231 Map
<String
, String
> map
= getMap(myResources
, version
, false);
233 String location
= map
.remove(url
);
234 if (location
!= null) {
235 myResourceLocations
.remove(url
);
237 myModificationCount
++;
238 fireExternalResourceChanged();
243 public void removeResource(String url
, @NotNull Project project
) {
244 getProjectResources(project
).removeResource(url
);
248 public void addResource(@NonNls String url
, @NonNls String location
, @NotNull Project project
) {
249 getProjectResources(project
).addResource(url
, location
);
252 public String
[] getAvailableUrls() {
253 Set
<String
> urls
= new HashSet
<String
>();
254 for (Map
<String
, String
> map
: myResources
.values()) {
255 urls
.addAll(map
.keySet());
257 return ArrayUtil
.toStringArray(urls
);
261 public String
[] getAvailableUrls(Project project
) {
262 return getProjectResources(project
).getAvailableUrls();
265 public void clearAllResources() {
267 myIgnoredResources
.clear();
270 public void clearAllResources(Project project
) {
272 getProjectResources(project
).clearAllResources();
273 myModificationCount
++;
274 fireExternalResourceChanged();
277 public void addIgnoredResource(String url
) {
278 addIgnoredSilently(url
);
279 fireExternalResourceChanged();
282 private void addIgnoredSilently(String url
) {
283 myIgnoredResources
.add(url
);
284 myModificationCount
++;
287 public void removeIgnoredResource(String url
) {
288 if (myIgnoredResources
.remove(url
)) {
289 myModificationCount
++;
290 fireExternalResourceChanged();
294 public boolean isIgnoredResource(String url
) {
295 return myIgnoredResources
.contains(url
) || isImplicitNamespaceDescriptor(url
);
298 private static boolean isImplicitNamespaceDescriptor(String url
) {
299 for (ImplicitNamespaceDescriptorProvider namespaceDescriptorProvider
: Extensions
300 .getExtensions(ImplicitNamespaceDescriptorProvider
.EP_NAME
)) {
301 if (namespaceDescriptorProvider
.getNamespaceDescriptor(null, url
) != null) return true;
306 public String
[] getIgnoredResources() {
307 return ArrayUtil
.toStringArray(myIgnoredResources
);
310 public long getModificationCount() {
311 return myModificationCount
;
314 public void readExternal(Element element
) throws InvalidDataException
{
315 final ExpandMacroToPathMap macroExpands
= new ExpandMacroToPathMap();
316 myPathMacros
.addMacroExpands(macroExpands
);
317 macroExpands
.substitute(element
, SystemInfo
.isFileSystemCaseSensitive
);
319 myModificationCount
++;
320 for (final Object o1
: element
.getChildren(RESOURCE_ELEMENT
)) {
321 Element e
= (Element
)o1
;
322 addSilently(e
.getAttributeValue(URL_ATTR
), DEFAULT_VERSION
, e
.getAttributeValue(LOCATION_ATTR
).replace('/', File
.separatorChar
));
325 for (final Object o
: element
.getChildren(IGNORED_RESOURCE_ELEMENT
)) {
326 Element e
= (Element
)o
;
327 addIgnoredSilently(e
.getAttributeValue(URL_ATTR
));
331 public void writeExternal(Element element
) throws WriteExternalException
{
332 final String
[] urls
= getAvailableUrls();
333 for (String url
: urls
) {
334 if (url
== null) continue;
335 String location
= getResourceLocation(url
);
336 if (location
== null) continue;
337 final Element e
= new Element(RESOURCE_ELEMENT
);
339 e
.setAttribute(URL_ATTR
, url
);
340 e
.setAttribute(LOCATION_ATTR
, location
.replace(File
.separatorChar
, '/'));
341 element
.addContent(e
);
344 final String
[] ignoredResources
= getIgnoredResources();
345 for (String ignoredResource
: ignoredResources
) {
346 final Element e
= new Element(IGNORED_RESOURCE_ELEMENT
);
348 e
.setAttribute(URL_ATTR
, ignoredResource
);
349 element
.addContent(e
);
352 final ReplacePathToMacroMap macroReplacements
= new ReplacePathToMacroMap();
353 PathMacrosImpl
.getInstanceEx().addMacroReplacements(macroReplacements
);
354 macroReplacements
.substitute(element
, SystemInfo
.isFileSystemCaseSensitive
);
357 public void addExternalResourceListener(ExternalResourceListener listener
) {
358 myListeners
.add(listener
);
361 public void removeExternalResourceListener(ExternalResourceListener listener
) {
362 myListeners
.remove(listener
);
365 private void fireExternalResourceChanged() {
366 for (ExternalResourceListener listener
: myListeners
.toArray(new ExternalResourceListener
[myListeners
.size()])) {
367 listener
.externalResourceChanged();
371 private static ExternalResourceManagerImpl
getProjectResources(Project project
) {
372 return ServiceManager
.getService(project
, ProjectResources
.class);