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
;
30 import javax
.swing
.event
.ChangeEvent
;
32 import java
.lang
.reflect
.InvocationHandler
;
33 import java
.lang
.reflect
.Method
;
34 import java
.lang
.reflect
.Proxy
;
36 import java
.util
.List
;
39 * @author Eugene Zhuravlev
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
) {
74 myModulesProvider
= modulesProvider
;
75 myFacetsConfigurator
= facetsConfigurator
;
76 addChangeListener(facetsConfigurator
);
78 myName
= module
.getName();
81 public void init(final String selectedTab
, History 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();
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()) {
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
);
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);
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
))) {
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() {
273 Disposer
.dispose(myDisposable
);
274 for (final ModuleConfigurationEditor myEditor
: myEditors
) {
275 myEditor
.disposeUIResources();
280 if (myTabbedPane
!= null) {
281 ourSelectedTabName
= getSelectedTabName();
286 myGenericSettingsPanel
= null;
288 return myModifiableRootModel
;
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
) {
311 public String
getName() {
316 public String
getSelectedTabName() {
317 return myTabbedPane
== null || myTabbedPane
.getSelectedIndex() == -1 ?
null : myTabbedPane
.getTitleAt(myTabbedPane
.getSelectedIndex());
320 public void setSelectedTabName(@Nullable String name
) {
323 final int editorTabIndex
= getEditorTabIndex(name
);
324 if (editorTabIndex
>= 0 && editorTabIndex
< myTabbedPane
.getTabCount()) {
325 myTabbedPane
.setSelectedIndex(editorTabIndex
);
326 ourSelectedTabName
= name
;
332 public ModuleConfigurationEditor
getEditor(@NotNull String tabName
) {
333 int index
= getEditorTabIndex(tabName
);
334 if (0 <= index
&& index
< myEditors
.size()) {
335 return myEditors
.get(index
);
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());
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
));
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());
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
];
393 (Library
)Proxy
.newProxyInstance(getClass().getClassLoader(), new Class
[]{Library
.class},
394 new LibraryInvocationHandler(library
));
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
));
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());
439 return method
.invoke(myDelegateModel
, unwrapParams(params
));
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());
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
];
465 (Library
)Proxy
.newProxyInstance(getClass().getClassLoader(), new Class
[]{Library
.class},
466 new LibraryInvocationHandler(library
));
469 if (result
instanceof Library
) {
471 Proxy
.newProxyInstance(getClass().getClassLoader(), new Class
[]{Library
.class},
472 new LibraryInvocationHandler((Library
)result
));
478 updateOrderEntriesInEditors();
484 public interface ProxyDelegateAccessor
{
485 Object
getDelegate();
488 private static Object
[] unwrapParams(Object
[] params
) {
489 if (params
== null || params
.length
== 0) {
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
;
507 public String
getHelpTopic() {
508 if (myTabbedPane
== null || myEditors
.isEmpty()) {
511 final int selectedIdx
= myTabbedPane
.getSelectedIndex();
512 if (selectedIdx
== -1) {
515 final ModuleConfigurationEditor moduleElementsEditor
= myEditors
.get(selectedIdx
);
516 return moduleElementsEditor
.getHelpTopic();
519 public void setModuleName(final String 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
)) {
537 public void setHistory(final History history
) {