migrated to artifacts
[fedora-idea.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / ModuleEditor.java
blob738a81ef10e5628e2393b0bfcac21353569c73b6
1 package com.intellij.openapi.roots.ui.configuration;
3 import com.intellij.facet.impl.ProjectFacetsConfigurator;
4 import com.intellij.openapi.Disposable;
5 import com.intellij.openapi.actionSystem.DataConstants;
6 import com.intellij.openapi.actionSystem.DataProvider;
7 import com.intellij.openapi.extensions.Extensions;
8 import com.intellij.openapi.module.Module;
9 import com.intellij.openapi.module.ModuleConfigurationEditor;
10 import com.intellij.openapi.module.impl.ModuleConfigurationStateImpl;
11 import com.intellij.openapi.options.ConfigurationException;
12 import com.intellij.openapi.project.Project;
13 import com.intellij.openapi.roots.ModifiableRootModel;
14 import com.intellij.openapi.roots.ModuleRootManager;
15 import com.intellij.openapi.roots.OrderEntry;
16 import com.intellij.openapi.roots.impl.ModuleRootManagerImpl;
17 import com.intellij.openapi.roots.libraries.Library;
18 import com.intellij.openapi.roots.libraries.LibraryTable;
19 import com.intellij.openapi.util.ActionCallback;
20 import com.intellij.openapi.util.Disposer;
21 import com.intellij.ui.TabbedPaneWrapper;
22 import com.intellij.ui.navigation.History;
23 import com.intellij.ui.navigation.Place;
24 import com.intellij.util.EventDispatcher;
25 import org.jetbrains.annotations.NonNls;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
29 import javax.swing.*;
30 import javax.swing.event.ChangeEvent;
31 import java.awt.*;
32 import java.lang.reflect.InvocationHandler;
33 import java.lang.reflect.Method;
34 import java.lang.reflect.Proxy;
35 import java.util.*;
36 import java.util.List;
38 /**
39 * @author Eugene Zhuravlev
40 * Date: Oct 4, 2003
41 * Time: 6:29:56 PM
43 @SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod"})
44 public class ModuleEditor implements Place.Navigator {
46 private final Project myProject;
47 private JPanel myGenericSettingsPanel;
48 private ModifiableRootModel myModifiableRootModel; // important: in order to correctly update OrderEntries UI use corresponding proxy for the model
50 private static String ourSelectedTabName;
52 private TabbedPaneWrapper myTabbedPane;
53 private final ModulesProvider myModulesProvider;
54 private String myName;
55 private final Module myModule;
57 private final List<ModuleConfigurationEditor> myEditors = new ArrayList<ModuleConfigurationEditor>();
58 private ModifiableRootModel myModifiableRootModelProxy;
60 private final EventDispatcher<ChangeListener> myEventDispatcher = EventDispatcher.create(ChangeListener.class);
61 @NonNls private static final String METHOD_COMMIT = "commit";
63 private History myHistory;
65 private final ProjectFacetsConfigurator myFacetsConfigurator;
66 private final Disposable myDisposable = new Disposable() {
67 public void dispose() {
71 public ModuleEditor(Project project, ModulesProvider modulesProvider, ProjectFacetsConfigurator facetsConfigurator,
72 @NotNull Module module) {
73 myProject = project;
74 myModulesProvider = modulesProvider;
75 myFacetsConfigurator = facetsConfigurator;
76 addChangeListener(facetsConfigurator);
77 myModule = module;
78 myName = module.getName();
81 public void init(final String selectedTab, History history) {
82 myHistory = history;
84 for (ModuleConfigurationEditor each : myEditors) {
85 if (each instanceof ModuleElementsEditor) {
86 ((ModuleElementsEditor)each).setHistory(myHistory);
90 setSelectedTabName(selectedTab);
94 public interface ChangeListener extends EventListener {
95 void moduleStateChanged(ModifiableRootModel moduleRootModel);
98 public void addChangeListener(ChangeListener listener) {
99 myEventDispatcher.addListener(listener);
102 public void removeChangeListener(ChangeListener listener) {
103 myEventDispatcher.removeListener(listener);
106 public Module getModule() {
107 final Module[] all = myModulesProvider.getModules();
108 for (Module each : all) {
109 if (each == myModule) return myModule;
112 return myModulesProvider.getModule(myName);
115 public ModifiableRootModel getModifiableRootModel() {
116 if (myModifiableRootModel == null){
117 myModifiableRootModel = ((ModuleRootManagerImpl)ModuleRootManager.getInstance(getModule())).getModifiableModel(new UIRootConfigurationAccessor(myProject));
119 return myModifiableRootModel;
122 public OrderEntry[] getOrderEntries() {
123 if (myModifiableRootModel == null) { // do not clone all model if not necessary
124 return ModuleRootManager.getInstance(getModule()).getOrderEntries();
126 else {
127 return myModifiableRootModel.getOrderEntries();
131 public ModifiableRootModel getModifiableRootModelProxy() {
132 if (myModifiableRootModelProxy == null) {
133 myModifiableRootModelProxy = (ModifiableRootModel)Proxy.newProxyInstance(
134 getClass().getClassLoader(), new Class[]{ModifiableRootModel.class}, new ModifiableRootModelInvocationHandler(getModifiableRootModel())
137 return myModifiableRootModelProxy;
140 public boolean isModified() {
141 for (ModuleConfigurationEditor moduleElementsEditor : myEditors) {
142 if (moduleElementsEditor.isModified()) {
143 return true;
146 return false;
149 private void createEditors(Module module) {
150 ModuleConfigurationEditorProvider[] providers = collectProviders(module);
151 ModuleConfigurationState state = createModuleConfigurationState();
152 List<ModuleLevelConfigurablesEditorProvider> moduleLevelProviders = new ArrayList<ModuleLevelConfigurablesEditorProvider>();
153 for (ModuleConfigurationEditorProvider provider : providers) {
154 if (provider instanceof ModuleLevelConfigurablesEditorProvider) {
155 moduleLevelProviders.add((ModuleLevelConfigurablesEditorProvider)provider);
156 continue;
158 processEditorsProvider(provider, state);
160 for (ModuleLevelConfigurablesEditorProvider provider : moduleLevelProviders) {
161 processEditorsProvider(provider, state);
165 private static ModuleConfigurationEditorProvider[] collectProviders(final Module module) {
166 List<ModuleConfigurationEditorProvider> result = new ArrayList<ModuleConfigurationEditorProvider>();
167 result.addAll(Arrays.asList(module.getComponents(ModuleConfigurationEditorProvider.class)));
168 result.addAll(Arrays.asList(Extensions.getExtensions(ModuleConfigurationEditorProvider.EP_NAME, module)));
169 return result.toArray(new ModuleConfigurationEditorProvider[result.size()]);
172 public ModuleConfigurationState createModuleConfigurationState() {
173 return new ModuleConfigurationStateImpl(myProject, myModulesProvider, getModifiableRootModelProxy(),
174 myFacetsConfigurator);
177 private void processEditorsProvider(final ModuleConfigurationEditorProvider provider, final ModuleConfigurationState state) {
178 final ModuleConfigurationEditor[] editors = provider.createEditors(state);
179 myEditors.addAll(Arrays.asList(editors));
182 private JPanel createPanel() {
183 getModifiableRootModel(); //initialize model if needed
184 getModifiableRootModelProxy();
186 myGenericSettingsPanel = new ModuleEditorPanel();
188 createEditors(getModule());
190 JPanel northPanel = new JPanel(new GridBagLayout());
192 myGenericSettingsPanel.add(northPanel, BorderLayout.NORTH);
194 myTabbedPane = new TabbedPaneWrapper(myDisposable);
196 for (ModuleConfigurationEditor editor : myEditors) {
197 myTabbedPane.addTab(editor.getDisplayName(), editor.getIcon(), editor.createComponent(), null);
198 editor.reset();
200 setSelectedTabName(ourSelectedTabName);
202 myGenericSettingsPanel.add(myTabbedPane.getComponent(), BorderLayout.CENTER);
203 myTabbedPane.addChangeListener(new javax.swing.event.ChangeListener() {
204 public void stateChanged(ChangeEvent e) {
205 ourSelectedTabName = getSelectedTabName();
206 if (myHistory != null) {
207 myHistory.pushQueryPlace();
212 return myGenericSettingsPanel;
215 public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
216 myTabbedPane.setSelectedTitle((String)place.getPath("moduleTab"));
217 return new ActionCallback.Done();
220 public void queryPlace(@NotNull final Place place) {
221 place.putPath("moduleTab", ourSelectedTabName);
224 public static String getSelectedTab(){
225 return ourSelectedTabName;
228 private int getEditorTabIndex(final String editorName) {
229 if (myTabbedPane != null && editorName != null) {
230 final int tabCount = myTabbedPane.getTabCount();
231 for (int idx = 0; idx < tabCount; idx++) {
232 if (editorName.equals(myTabbedPane.getTitleAt(idx))) {
233 return idx;
237 return -1;
240 public JPanel getPanel() {
241 if (myGenericSettingsPanel == null) {
242 myGenericSettingsPanel = createPanel();
245 return myGenericSettingsPanel;
248 public void moduleCountChanged() {
249 updateOrderEntriesInEditors();
252 private void updateOrderEntriesInEditors() {
253 if (getModule() != null) { //module with attached module libraries was deleted
254 getPanel(); //init editor if needed
255 for (final ModuleConfigurationEditor myEditor : myEditors) {
256 myEditor.moduleStateChanged();
258 myEventDispatcher.getMulticaster().moduleStateChanged(getModifiableRootModelProxy());
262 public void updateCompilerOutputPathChanged(String baseUrl, String moduleName){
263 if (myGenericSettingsPanel == null) return; //wasn't initialized yet
264 for (final ModuleConfigurationEditor myEditor : myEditors) {
265 if (myEditor instanceof ModuleElementsEditor) {
266 ((ModuleElementsEditor)myEditor).moduleCompileOutputChanged(baseUrl, moduleName);
271 public ModifiableRootModel dispose() {
272 try {
273 Disposer.dispose(myDisposable);
274 for (final ModuleConfigurationEditor myEditor : myEditors) {
275 myEditor.disposeUIResources();
278 myEditors.clear();
280 if (myTabbedPane != null) {
281 ourSelectedTabName = getSelectedTabName();
282 myTabbedPane = null;
286 myGenericSettingsPanel = null;
288 return myModifiableRootModel;
290 finally {
291 myModifiableRootModel = null;
292 myModifiableRootModelProxy = null;
296 public ModifiableRootModel applyAndDispose() throws ConfigurationException {
297 for (ModuleConfigurationEditor editor : myEditors) {
298 if (editor instanceof ModuleElementsEditor) {
299 ((ModuleElementsEditor)editor).canApply();
303 for (ModuleConfigurationEditor editor : myEditors) {
304 editor.saveData();
305 editor.apply();
308 return dispose();
311 public String getName() {
312 return myName;
315 @Nullable
316 public String getSelectedTabName() {
317 return myTabbedPane == null || myTabbedPane.getSelectedIndex() == -1 ? null : myTabbedPane.getTitleAt(myTabbedPane.getSelectedIndex());
320 public void setSelectedTabName(@Nullable String name) {
321 if (name != null) {
322 getPanel();
323 final int editorTabIndex = getEditorTabIndex(name);
324 if (editorTabIndex >= 0 && editorTabIndex < myTabbedPane.getTabCount()) {
325 myTabbedPane.setSelectedIndex(editorTabIndex);
326 ourSelectedTabName = name;
331 @Nullable
332 public ModuleConfigurationEditor getEditor(@NotNull String tabName) {
333 int index = getEditorTabIndex(tabName);
334 if (0 <= index && index < myEditors.size()) {
335 return myEditors.get(index);
337 return null;
340 private class ModifiableRootModelInvocationHandler implements InvocationHandler {
341 private final ModifiableRootModel myDelegateModel;
342 @NonNls private final Set<String> myCheckedNames = new HashSet<String>(
343 Arrays.asList("addOrderEntry", "addLibraryEntry", "addInvalidLibrary", "addModuleOrderEntry", "addInvalidModuleEntry",
344 "removeOrderEntry", "setSdk", "inheritSdk", "inheritCompilerOutputPath", "setExcludeOutput", "replaceEntryOfType"));
346 ModifiableRootModelInvocationHandler(ModifiableRootModel model) {
347 myDelegateModel = model;
350 public Object invoke(Object object, Method method, Object[] params) throws Throwable {
351 final boolean needUpdate = myCheckedNames.contains(method.getName());
352 try {
353 final Object result = method.invoke(myDelegateModel, unwrapParams(params));
354 if (result instanceof LibraryTable) {
355 return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{LibraryTable.class},
356 new LibraryTableInvocationHandler((LibraryTable)result));
358 return result;
360 finally {
361 if (needUpdate) {
362 updateOrderEntriesInEditors();
368 private class LibraryTableInvocationHandler implements InvocationHandler {
369 private final LibraryTable myDelegateTable;
370 @NonNls private final Set<String> myCheckedNames = new HashSet<String>(Arrays.asList("removeLibrary" /*,"createLibrary"*/));
372 LibraryTableInvocationHandler(LibraryTable table) {
373 myDelegateTable = table;
376 public Object invoke(Object object, Method method, Object[] params) throws Throwable {
377 final boolean needUpdate = myCheckedNames.contains(method.getName());
378 try {
379 final Object result = method.invoke(myDelegateTable, unwrapParams(params));
380 if (result instanceof Library) {
381 return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Library.class},
382 new LibraryInvocationHandler((Library)result));
384 else if (result instanceof LibraryTable.ModifiableModel) {
385 return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{LibraryTable.ModifiableModel.class},
386 new LibraryTableModelInvocationHandler((LibraryTable.ModifiableModel)result));
388 if (result instanceof Library[]) {
389 Library[] libraries = (Library[])result;
390 for (int idx = 0; idx < libraries.length; idx++) {
391 Library library = libraries[idx];
392 libraries[idx] =
393 (Library)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Library.class},
394 new LibraryInvocationHandler(library));
397 return result;
399 finally {
400 if (needUpdate) {
401 updateOrderEntriesInEditors();
408 private class LibraryInvocationHandler implements InvocationHandler, ProxyDelegateAccessor {
409 private final Library myDelegateLibrary;
411 LibraryInvocationHandler(Library delegateLibrary) {
412 myDelegateLibrary = delegateLibrary;
415 public Object invoke(Object object, Method method, Object[] params) throws Throwable {
416 final Object result = method.invoke(myDelegateLibrary, unwrapParams(params));
417 if (result instanceof Library.ModifiableModel) {
418 return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Library.ModifiableModel.class},
419 new LibraryModifiableModelInvocationHandler((Library.ModifiableModel)result));
421 return result;
424 public Object getDelegate() {
425 return myDelegateLibrary;
429 private class LibraryModifiableModelInvocationHandler implements InvocationHandler {
430 private final Library.ModifiableModel myDelegateModel;
432 LibraryModifiableModelInvocationHandler(Library.ModifiableModel delegateModel) {
433 myDelegateModel = delegateModel;
436 public Object invoke(Object object, Method method, Object[] params) throws Throwable {
437 final boolean needUpdate = METHOD_COMMIT.equals(method.getName());
438 try {
439 return method.invoke(myDelegateModel, unwrapParams(params));
441 finally {
442 if (needUpdate) {
443 updateOrderEntriesInEditors();
449 private class LibraryTableModelInvocationHandler implements InvocationHandler {
450 private final LibraryTable.ModifiableModel myDelegateModel;
452 LibraryTableModelInvocationHandler(LibraryTable.ModifiableModel delegateModel) {
453 myDelegateModel = delegateModel;
456 public Object invoke(Object object, Method method, Object[] params) throws Throwable {
457 final boolean needUpdate = METHOD_COMMIT.equals(method.getName());
458 try {
459 Object result = method.invoke(myDelegateModel, unwrapParams(params));
460 if (result instanceof Library[]) {
461 Library[] libraries = (Library[])result;
462 for (int idx = 0; idx < libraries.length; idx++) {
463 Library library = libraries[idx];
464 libraries[idx] =
465 (Library)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Library.class},
466 new LibraryInvocationHandler(library));
469 if (result instanceof Library) {
470 result =
471 Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Library.class},
472 new LibraryInvocationHandler((Library)result));
474 return result;
476 finally {
477 if (needUpdate) {
478 updateOrderEntriesInEditors();
484 public interface ProxyDelegateAccessor {
485 Object getDelegate();
488 private static Object[] unwrapParams(Object[] params) {
489 if (params == null || params.length == 0) {
490 return params;
492 final Object[] unwrappedParams = new Object[params.length];
493 for (int idx = 0; idx < params.length; idx++) {
494 Object param = params[idx];
495 if (param != null && Proxy.isProxyClass(param.getClass())) {
496 final InvocationHandler invocationHandler = Proxy.getInvocationHandler(param);
497 if (invocationHandler instanceof ProxyDelegateAccessor) {
498 param = ((ProxyDelegateAccessor)invocationHandler).getDelegate();
501 unwrappedParams[idx] = param;
503 return unwrappedParams;
506 @Nullable
507 public String getHelpTopic() {
508 if (myTabbedPane == null || myEditors.isEmpty()) {
509 return null;
511 final int selectedIdx = myTabbedPane.getSelectedIndex();
512 if (selectedIdx == -1) {
513 return null;
515 final ModuleConfigurationEditor moduleElementsEditor = myEditors.get(selectedIdx);
516 return moduleElementsEditor.getHelpTopic();
519 public void setModuleName(final String name) {
520 myName = name;
523 private class ModuleEditorPanel extends JPanel implements DataProvider{
524 public ModuleEditorPanel() {
525 super(new BorderLayout());
528 public Object getData(String dataId) {
529 if (dataId.equals(DataConstants.MODULE_CONTEXT)) {
530 return getModule();
532 return null;
537 public void setHistory(final History history) {