| /* |
| * Copyright 2000-2013 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.uiDesigner; |
| |
| import com.intellij.ProjectTopics; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.components.ServiceManager; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleUtil; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.roots.*; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.vfs.JarFileSystem; |
| import com.intellij.openapi.vfs.VfsUtil; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.vfs.VirtualFileManager; |
| import com.intellij.openapi.vfs.impl.jar.JarFileSystemImpl; |
| import com.intellij.uiDesigner.core.Spacer; |
| import com.intellij.util.PathUtil; |
| import com.intellij.util.containers.ConcurrentWeakHashMap; |
| import com.intellij.util.lang.UrlClassLoader; |
| import com.intellij.util.messages.MessageBusConnection; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.io.File; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.*; |
| |
| /** |
| * @author Anton Katilin |
| * @author Vladimir Kondratyev |
| */ |
| public final class LoaderFactory { |
| private final Project myProject; |
| |
| private final ConcurrentWeakHashMap<Module, ClassLoader> myModule2ClassLoader; |
| private ClassLoader myProjectClassLoader = null; |
| private final MessageBusConnection myConnection; |
| |
| public static LoaderFactory getInstance(final Project project) { |
| return ServiceManager.getService(project, LoaderFactory.class); |
| } |
| |
| public LoaderFactory(final Project project) { |
| myProject = project; |
| myModule2ClassLoader = new ConcurrentWeakHashMap<Module, ClassLoader>(); |
| myConnection = myProject.getMessageBus().connect(); |
| myConnection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() { |
| public void rootsChanged(final ModuleRootEvent event) { |
| clearClassLoaderCache(); |
| } |
| }); |
| |
| Disposer.register(project, new Disposable() { |
| public void dispose() { |
| myConnection.disconnect(); |
| myModule2ClassLoader.clear(); |
| } |
| }); |
| } |
| |
| @NotNull public ClassLoader getLoader(final VirtualFile formFile) { |
| final Module module = ModuleUtil.findModuleForFile(formFile, myProject); |
| if (module == null) { |
| return getClass().getClassLoader(); |
| } |
| |
| return getLoader(module); |
| } |
| |
| public ClassLoader getLoader(final Module module) { |
| final ClassLoader cachedLoader = myModule2ClassLoader.get(module); |
| if (cachedLoader != null) { |
| return cachedLoader; |
| } |
| |
| final String runClasspath = OrderEnumerator.orderEntries(module).recursively().getPathsList().getPathsString(); |
| |
| final ClassLoader classLoader = createClassLoader(runClasspath, module.getName()); |
| |
| myModule2ClassLoader.put(module, classLoader); |
| |
| return classLoader; |
| } |
| |
| @NotNull public ClassLoader getProjectClassLoader() { |
| if (myProjectClassLoader == null) { |
| final String runClasspath = OrderEnumerator.orderEntries(myProject).withoutSdk().getPathsList().getPathsString(); |
| myProjectClassLoader = createClassLoader(runClasspath, "<project>"); |
| } |
| return myProjectClassLoader; |
| } |
| |
| private static ClassLoader createClassLoader(final String runClasspath, final String moduleName) { |
| final ArrayList<URL> urls = new ArrayList<URL>(); |
| final VirtualFileManager manager = VirtualFileManager.getInstance(); |
| final JarFileSystemImpl fileSystem = (JarFileSystemImpl)JarFileSystem.getInstance(); |
| final StringTokenizer tokenizer = new StringTokenizer(runClasspath, File.pathSeparator); |
| while (tokenizer.hasMoreTokens()) { |
| final String s = tokenizer.nextToken(); |
| try { |
| VirtualFile vFile = manager.findFileByUrl(VfsUtil.pathToUrl(s)); |
| final File realFile = fileSystem.getMirroredFile(vFile); |
| urls.add(realFile != null ? realFile.toURI().toURL() : new File(s).toURI().toURL()); |
| } |
| catch (Exception e) { |
| // ignore ? |
| } |
| } |
| |
| try { |
| urls.add(new File(PathUtil.getJarPathForClass(Spacer.class)).toURI().toURL()); |
| } |
| catch (MalformedURLException ignored) { |
| // ignore |
| } |
| |
| final URL[] _urls = urls.toArray(new URL[urls.size()]); |
| return new DesignTimeClassLoader(Arrays.asList(_urls), LoaderFactory.class.getClassLoader(), moduleName); |
| } |
| |
| public void clearClassLoaderCache() { |
| // clear classes with invalid classloader from UIManager cache |
| final UIDefaults uiDefaults = UIManager.getDefaults(); |
| for (Iterator it = uiDefaults.keySet().iterator(); it.hasNext();) { |
| Object key = it.next(); |
| Object value = uiDefaults.get(key); |
| if (value instanceof Class) { |
| ClassLoader loader = ((Class)value).getClassLoader(); |
| if (loader instanceof DesignTimeClassLoader) { |
| it.remove(); |
| } |
| } |
| } |
| myModule2ClassLoader.clear(); |
| myProjectClassLoader = null; |
| } |
| |
| private static class DesignTimeClassLoader extends UrlClassLoader { |
| private final String myModuleName; |
| |
| public DesignTimeClassLoader(final List<URL> urls, final ClassLoader parent, final String moduleName) { |
| super(build().urls(urls).parent(parent)); |
| myModuleName = moduleName; |
| } |
| |
| @Override |
| public String toString() { |
| return "DesignTimeClassLoader:" + myModuleName; |
| } |
| } |
| } |