lvcs-impl
[idea/community.git] / lang-api / src / com / intellij / ide / util / projectWizard / ModuleBuilder.java
1 /*
2  * Copyright 2000-2007 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.ide.util.projectWizard;
17
18 import com.intellij.ide.IdeBundle;
19 import com.intellij.ide.highlighter.ModuleFileType;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.extensions.ExtensionPointName;
23 import com.intellij.openapi.module.*;
24 import com.intellij.openapi.options.ConfigurationException;
25 import com.intellij.openapi.project.DumbAwareRunnable;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.roots.ModifiableRootModel;
28 import com.intellij.openapi.roots.ModuleRootManager;
29 import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
30 import com.intellij.openapi.startup.StartupManager;
31 import com.intellij.openapi.ui.Messages;
32 import com.intellij.openapi.util.Computable;
33 import com.intellij.openapi.util.InvalidDataException;
34 import com.intellij.openapi.util.Ref;
35 import com.intellij.openapi.util.io.FileUtil;
36 import com.intellij.openapi.vfs.LocalFileSystem;
37 import com.intellij.openapi.vfs.VirtualFile;
38 import com.intellij.util.EventDispatcher;
39 import org.jdom.JDOMException;
40 import org.jetbrains.annotations.NonNls;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43
44 import javax.swing.*;
45 import java.io.File;
46 import java.io.IOException;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.List;
50
51 public abstract class ModuleBuilder extends ProjectBuilder{
52   private static final ExtensionPointName<ModuleBuilderFactory> EP_NAME = ExtensionPointName.create("com.intellij.moduleBuilder");
53
54   private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.projectWizard.ModuleBuilder");
55   private String myName;
56   @NonNls private String myModuleFilePath;
57   @Nullable
58   private final List<ModuleConfigurationUpdater> myUpdaters = new ArrayList<ModuleConfigurationUpdater>();
59   private final EventDispatcher<ModuleBuilderListener> myDispatcher = EventDispatcher.create(ModuleBuilderListener.class);
60
61   public static List<ModuleBuilder> getAllBuilders() {
62     final ArrayList<ModuleBuilder> result = new ArrayList<ModuleBuilder>();
63     for (final ModuleType moduleType : ModuleTypeManager.getInstance().getRegisteredTypes()) {
64       result.add(moduleType.createModuleBuilder());
65     }
66     for (ModuleBuilderFactory factory : EP_NAME.getExtensions()) {
67       result.add(factory.createBuilder());
68     }
69     return result;
70   }
71
72   @Nullable
73   protected final String acceptParameter(String param) {
74     return param != null && param.length() > 0 ? param : null;
75   }
76
77   public String getName() {
78     return myName;
79   }
80
81   public String getBuilderId() {
82     return getModuleType().getId();
83   }
84
85   public ModuleWizardStep[] createWizardSteps(WizardContext wizardContext, ModulesProvider modulesProvider) {
86     return getModuleType().createWizardSteps(wizardContext, this, modulesProvider);
87   }
88
89   public void setName(String name) {
90     myName = acceptParameter(name);
91   }
92
93   public String getModuleFilePath() {
94     return myModuleFilePath;
95   }
96
97   public void addModuleConfigurationUpdater(ModuleConfigurationUpdater updater) {
98     myUpdaters.add(updater);
99   }
100
101   public void setModuleFilePath(@NonNls String path) {
102     myModuleFilePath = acceptParameter(path);
103   }
104
105   @Nullable
106   public String getModuleFileDirectory() {
107     if (myModuleFilePath == null) {
108       return null;
109     }
110     final String parent = new File(myModuleFilePath).getParent();
111     if (parent == null) {
112       return null;
113     }
114     return parent.replace(File.separatorChar, '/');
115   }
116
117   @NotNull
118   public Module createModule(ModifiableModuleModel moduleModel)
119     throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException {
120     LOG.assertTrue(myName != null);
121     LOG.assertTrue(myModuleFilePath != null);
122
123     deleteModuleFile(myModuleFilePath);
124     final ModuleType moduleType = getModuleType();
125     final Module module = moduleModel.newModule(myModuleFilePath, moduleType);
126     final ModifiableRootModel modifiableModel = ModuleRootManager.getInstance(module).getModifiableModel();
127     setupRootModel(modifiableModel);
128     for (ModuleConfigurationUpdater updater : myUpdaters) {
129       updater.update(module, modifiableModel);
130     }
131     modifiableModel.commit();
132
133     module.setSavePathsRelative(true); // default setting
134
135     return module;
136   }
137
138   private void onModuleInitialized(final Module module) {
139     myDispatcher.getMulticaster().moduleCreated(module);
140   }
141
142   public abstract void setupRootModel(ModifiableRootModel modifiableRootModel) throws ConfigurationException;
143
144   public abstract ModuleType getModuleType();
145
146   @NotNull
147   public Module createAndCommitIfNeeded(final Project project, ModifiableModuleModel model, boolean runFromProjectWizard) throws
148                                                                                                  InvalidDataException,
149                                                                                                  ConfigurationException,
150                                                                                                  IOException,
151                                                                                                  JDOMException,
152                                                                                                  ModuleWithNameAlreadyExists{
153     final ModifiableModuleModel moduleModel = model != null ? model : ModuleManager.getInstance(project).getModifiableModel();
154     final Module module = createModule(moduleModel);
155     if (model == null) moduleModel.commit();
156
157     if (runFromProjectWizard) {
158       StartupManager.getInstance(module.getProject()).registerPostStartupActivity(new DumbAwareRunnable() {
159       public void run() {
160         ApplicationManager.getApplication().runWriteAction(new Runnable() {
161           public void run() {
162             onModuleInitialized(module);
163           }
164         });
165       }
166     });
167     }
168     else {
169       onModuleInitialized(module);
170     }
171     return module;
172   }
173
174
175   public void addListener(ModuleBuilderListener listener) {
176     myDispatcher.addListener(listener);
177   }
178
179   public void removeListener(ModuleBuilderListener listener) {
180     myDispatcher.removeListener(listener);
181   }
182
183   public boolean canCreateModule() {
184     return true;
185   }
186
187   @Nullable
188   public List<Module> commit(final Project project, final ModifiableModuleModel model, final ModulesProvider modulesProvider) {
189     final Module module = commitModule(project, model);
190     return module != null ? Collections.singletonList(module) : null;
191   }
192
193   public Module commitModule(final Project project, final ModifiableModuleModel model) {
194     final Ref<Module> result = new Ref<Module>();
195     if (canCreateModule()) {
196       if (myName == null) {
197         myName = project.getName();
198       }
199       if (myModuleFilePath == null) {
200         myModuleFilePath = project.getBaseDir().getPath() + File.separator + myName + ModuleFileType.DOT_DEFAULT_EXTENSION;
201       }
202       Exception ex = ApplicationManager.getApplication().runWriteAction(new Computable<Exception>() {
203         public Exception compute() {
204           try {
205             result.set(createAndCommitIfNeeded(project, model, true));
206             return null;
207           }
208           catch (Exception e) {
209             return e;
210           }
211         }
212       });
213       if (ex != null) {
214         LOG.info(ex);
215         Messages.showErrorDialog(IdeBundle.message("error.adding.module.to.project", ex.getMessage()), IdeBundle.message("title.add.module"));
216       }
217     }
218     return result.get();
219   }
220
221   public static void deleteModuleFile(String moduleFilePath) {
222     final File moduleFile = new File(moduleFilePath);
223     if (moduleFile.exists()) {
224       FileUtil.delete(moduleFile);
225     }
226     final VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(moduleFile);
227     if (file != null) {
228       file.refresh(false, false);
229     }
230   }
231
232   public Icon getBigIcon() {
233     return getModuleType().getBigIcon();
234   }
235
236   public String getDescription() {
237     return getModuleType().getDescription();
238   }
239
240   public String getPresentableName() {
241     return getModuleType().getName();
242   }
243
244   public static abstract class ModuleConfigurationUpdater {
245
246     public abstract void update(Module module, ModifiableRootModel rootModel);
247
248   }
249 }