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
.uiDesigner
.actions
;
18 import com
.intellij
.CommonBundle
;
19 import com
.intellij
.compiler
.PsiClassWriter
;
20 import com
.intellij
.compiler
.impl
.FileSetCompileScope
;
21 import com
.intellij
.execution
.*;
22 import com
.intellij
.execution
.configurations
.*;
23 import com
.intellij
.execution
.executors
.DefaultRunExecutor
;
24 import com
.intellij
.execution
.filters
.TextConsoleBuilderFactory
;
25 import com
.intellij
.execution
.runners
.ExecutionEnvironment
;
26 import com
.intellij
.execution
.runners
.ProgramRunner
;
27 import com
.intellij
.lang
.properties
.PropertiesFileType
;
28 import com
.intellij
.lang
.properties
.PropertiesReferenceManager
;
29 import com
.intellij
.lang
.properties
.psi
.PropertiesFile
;
30 import com
.intellij
.openapi
.actionSystem
.AnAction
;
31 import com
.intellij
.openapi
.actionSystem
.AnActionEvent
;
32 import com
.intellij
.openapi
.actionSystem
.DataContext
;
33 import com
.intellij
.openapi
.actionSystem
.PlatformDataKeys
;
34 import com
.intellij
.openapi
.application
.PathManager
;
35 import com
.intellij
.openapi
.compiler
.CompileContext
;
36 import com
.intellij
.openapi
.compiler
.CompileStatusNotification
;
37 import com
.intellij
.openapi
.compiler
.CompilerManager
;
38 import com
.intellij
.openapi
.diagnostic
.Logger
;
39 import com
.intellij
.openapi
.editor
.Document
;
40 import com
.intellij
.openapi
.fileEditor
.FileDocumentManager
;
41 import com
.intellij
.openapi
.fileTypes
.FileTypeManager
;
42 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
43 import com
.intellij
.openapi
.module
.Module
;
44 import com
.intellij
.openapi
.module
.ModuleUtil
;
45 import com
.intellij
.openapi
.project
.Project
;
46 import com
.intellij
.openapi
.roots
.ProjectClasspathTraversing
;
47 import com
.intellij
.openapi
.roots
.ProjectRootsTraversing
;
48 import com
.intellij
.openapi
.ui
.Messages
;
49 import com
.intellij
.openapi
.util
.io
.FileUtil
;
50 import com
.intellij
.openapi
.vfs
.VirtualFile
;
51 import com
.intellij
.openapi
.wm
.WindowManager
;
52 import com
.intellij
.uiDesigner
.FormEditingUtil
;
53 import com
.intellij
.uiDesigner
.UIDesignerBundle
;
54 import com
.intellij
.uiDesigner
.compiler
.AsmCodeGenerator
;
55 import com
.intellij
.uiDesigner
.compiler
.FormErrorInfo
;
56 import com
.intellij
.uiDesigner
.compiler
.Utils
;
57 import com
.intellij
.uiDesigner
.designSurface
.GuiEditor
;
58 import com
.intellij
.uiDesigner
.lw
.*;
59 import com
.intellij
.uiDesigner
.make
.CopyResourcesUtil
;
60 import com
.intellij
.uiDesigner
.make
.Form2ByteCodeCompiler
;
61 import com
.intellij
.uiDesigner
.make
.PreviewNestedFormLoader
;
62 import com
.intellij
.util
.PathsList
;
63 import com
.intellij
.util
.containers
.HashSet
;
64 import org
.jetbrains
.annotations
.NonNls
;
65 import org
.jetbrains
.annotations
.NotNull
;
66 import org
.jetbrains
.annotations
.Nullable
;
70 import java
.io
.IOException
;
71 import java
.util
.List
;
72 import java
.util
.Locale
;
75 * @author Anton Katilin
76 * @author Vladimir Kondratyev
78 public final class PreviewFormAction
extends AnAction
{
79 private static final Logger LOG
= Logger
.getInstance("#com.intellij.uiDesigner.actions.PreviewFormAction");
82 * The problem is that this class is in a default package so it's not
83 * import this class to refer
85 private static final String CLASS_TO_BIND_NAME
= "FormPreviewFrame";
86 @NonNls private static final String RUNTIME_BUNDLE_PREFIX
= "RuntimeBundle";
87 @NonNls public static final String PREVIEW_BINDING_FIELD
= "myComponent";
89 public void actionPerformed(final AnActionEvent e
) {
90 final GuiEditor editor
= FormEditingUtil
.getActiveEditor(e
.getDataContext());
92 showPreviewFrame(editor
.getModule(), editor
.getFile(), e
.getDataContext(), editor
.getStringDescriptorLocale());
96 public void update(final AnActionEvent e
) {
97 final GuiEditor editor
= FormEditingUtil
.getActiveEditor(e
.getDataContext());
100 e
.getPresentation().setVisible(false);
104 final VirtualFile file
= editor
.getFile();
105 e
.getPresentation().setVisible(
106 FileDocumentManager
.getInstance().getDocument(file
) != null &&
107 FileTypeManager
.getInstance().getFileTypeByFile(file
) == StdFileTypes
.GUI_DESIGNER_FORM
111 private static void showPreviewFrame(@NotNull final Module module
, @NotNull final VirtualFile formFile
,
112 final DataContext dataContext
, @Nullable final Locale stringDescriptorLocale
) {
113 final String tempPath
;
115 final File tempDirectory
= FileUtil
.createTempDirectory("FormPreview", "");
116 tempPath
= tempDirectory
.getAbsolutePath();
118 CopyResourcesUtil
.copyFormsRuntime(tempPath
, true);
120 catch (IOException e
) {
121 Messages
.showErrorDialog(
123 UIDesignerBundle
.message("error.cannot.preview.form", formFile
.getPath().replace('/', File
.separatorChar
), e
.toString()),
124 CommonBundle
.getErrorTitle()
129 final PathsList sources
= ProjectRootsTraversing
.collectRoots(module
, ProjectRootsTraversing
.PROJECT_SOURCES
);
130 final String classPath
=
131 ProjectRootsTraversing
.collectRoots(module
, ProjectClasspathTraversing
.FULL_CLASSPATH_RECURSIVE
).getPathsString() + File
.pathSeparator
+
132 sources
.getPathsString() + File
.pathSeparator
+ /* resources bundles */
134 final ClassLoader loader
= Form2ByteCodeCompiler
.createClassLoader(classPath
);
136 final Document doc
= FileDocumentManager
.getInstance().getDocument(formFile
);
137 final LwRootContainer rootContainer
;
139 rootContainer
= Utils
.getRootContainer(doc
.getText(), new CompiledClassPropertiesProvider(loader
));
141 catch (Exception e
) {
142 Messages
.showErrorDialog(
144 UIDesignerBundle
.message("error.cannot.read.form", formFile
.getPath().replace('/', File
.separatorChar
), e
.getMessage()),
145 CommonBundle
.getErrorTitle()
150 if (rootContainer
.getComponentCount() == 0) {
151 Messages
.showErrorDialog(
153 UIDesignerBundle
.message("error.cannot.preview.empty.form", formFile
.getPath().replace('/', File
.separatorChar
)),
154 CommonBundle
.getErrorTitle()
159 setPreviewBindings(rootContainer
, CLASS_TO_BIND_NAME
);
161 // 2. Copy previewer class and all its superclasses into TEMP directory and instrument it.
163 PreviewNestedFormLoader nestedFormLoader
= new PreviewNestedFormLoader(module
, tempPath
, loader
);
165 final File tempFile
= CopyResourcesUtil
.copyClass(tempPath
, CLASS_TO_BIND_NAME
, true);
166 //CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$1", true);
167 CopyResourcesUtil
.copyClass(tempPath
, CLASS_TO_BIND_NAME
+ "$MyExitAction", true);
168 CopyResourcesUtil
.copyClass(tempPath
, CLASS_TO_BIND_NAME
+ "$MyPackAction", true);
169 CopyResourcesUtil
.copyClass(tempPath
, CLASS_TO_BIND_NAME
+ "$MySetLafAction", true);
171 Locale locale
= Locale
.getDefault();
172 if (locale
.getCountry().length() > 0 && locale
.getLanguage().length() > 0) {
173 CopyResourcesUtil
.copyProperties(tempPath
, RUNTIME_BUNDLE_PREFIX
+ "_" + locale
.getLanguage() +
174 "_" + locale
.getCountry() + PropertiesFileType
.DOT_DEFAULT_EXTENSION
);
176 if (locale
.getLanguage().length() > 0) {
177 CopyResourcesUtil
.copyProperties(tempPath
, RUNTIME_BUNDLE_PREFIX
+ "_" + locale
.getLanguage() + PropertiesFileType
.DOT_DEFAULT_EXTENSION
);
179 CopyResourcesUtil
.copyProperties(tempPath
, RUNTIME_BUNDLE_PREFIX
+ "_" + locale
.getLanguage() + PropertiesFileType
.DOT_DEFAULT_EXTENSION
);
180 CopyResourcesUtil
.copyProperties(tempPath
, RUNTIME_BUNDLE_PREFIX
+ PropertiesFileType
.DOT_DEFAULT_EXTENSION
);
182 final AsmCodeGenerator codeGenerator
= new AsmCodeGenerator(rootContainer
, loader
, nestedFormLoader
, true,
183 new PsiClassWriter(module
));
184 codeGenerator
.patchFile(tempFile
);
185 final FormErrorInfo
[] errors
= codeGenerator
.getErrors();
186 if(errors
.length
!= 0){
187 Messages
.showErrorDialog(
189 UIDesignerBundle
.message("error.cannot.preview.form",
190 formFile
.getPath().replace('/', File
.separatorChar
),
191 errors
[0].getErrorMessage()),
192 CommonBundle
.getErrorTitle()
197 catch (Exception e
) {
199 Messages
.showErrorDialog(
201 UIDesignerBundle
.message("error.cannot.preview.form", formFile
.getPath().replace('/', File
.separatorChar
),
202 e
.getMessage() != null ? e
.getMessage() : e
.toString()),
203 CommonBundle
.getErrorTitle()
208 // 2.5. Copy up-to-date properties files to the output directory.
209 final HashSet
<String
> bundleSet
= new HashSet
<String
>();
210 FormEditingUtil
.iterateStringDescriptors(
212 new FormEditingUtil
.StringDescriptorVisitor
<IComponent
>() {
213 public boolean visit(final IComponent component
, final StringDescriptor descriptor
) {
214 if (descriptor
.getBundleName() != null) {
215 bundleSet
.add(descriptor
.getDottedBundleName());
221 if (bundleSet
.size() > 0) {
222 HashSet
<VirtualFile
> virtualFiles
= new HashSet
<VirtualFile
>();
223 HashSet
<Module
> modules
= new HashSet
<Module
>();
224 PropertiesReferenceManager manager
= PropertiesReferenceManager
.getInstance(module
.getProject());
225 for(String bundleName
: bundleSet
) {
226 for(PropertiesFile propFile
: manager
.findPropertiesFiles(module
, bundleName
)) {
227 virtualFiles
.add(propFile
.getVirtualFile());
228 modules
.add(ModuleUtil
.findModuleForFile(propFile
.getVirtualFile(), module
.getProject()));
231 FileSetCompileScope scope
= new FileSetCompileScope(virtualFiles
, modules
.toArray(new Module
[]{}));
233 CompilerManager
.getInstance(module
.getProject()).make(scope
, new CompileStatusNotification() {
234 public void finished(boolean aborted
, int errors
, int warnings
, final CompileContext compileContext
) {
235 if (!aborted
&& errors
== 0) {
236 runPreviewProcess(tempPath
, sources
, module
, formFile
, dataContext
, stringDescriptorLocale
);
242 runPreviewProcess(tempPath
, sources
, module
, formFile
, dataContext
, stringDescriptorLocale
);
246 public static void setPreviewBindings(final LwRootContainer rootContainer
, final String classToBindName
) {
247 // 1. Prepare form to preview. We have to change container so that it has only one binding.
248 rootContainer
.setClassToBind(classToBindName
);
249 FormEditingUtil
.iterate(
251 new FormEditingUtil
.ComponentVisitor
<LwComponent
>() {
252 public boolean visit(final LwComponent iComponent
) {
253 iComponent
.setBinding(null);
258 if (rootContainer
.getComponentCount() == 1) {
259 //noinspection HardCodedStringLiteral
260 ((LwComponent
)rootContainer
.getComponent(0)).setBinding(PREVIEW_BINDING_FIELD
);
264 private static void runPreviewProcess(final String tempPath
, final PathsList sources
, final Module module
, final VirtualFile formFile
,
265 final DataContext dataContext
, @Nullable final Locale stringDescriptorLocale
) {
266 // 3. Now we are ready to launch Java process
267 final JavaParameters parameters
= new JavaParameters();
268 parameters
.getClassPath().add(tempPath
);
269 parameters
.getClassPath().add(PathManager
.findFileInLibDirectory("jgoodies-forms.jar").getAbsolutePath());
270 final List
<String
> paths
= sources
.getPathList();
271 for (final String path
: paths
) {
272 parameters
.getClassPath().add(path
);
275 parameters
.configureByModule(module
, JavaParameters
.JDK_AND_CLASSES
);
277 catch (CantRunException e
) {
278 Messages
.showErrorDialog(
280 UIDesignerBundle
.message("error.cannot.preview.form", formFile
.getPath().replace('/', File
.separatorChar
), e
.getMessage()),
281 CommonBundle
.getErrorTitle()
285 parameters
.setMainClass("FormPreviewFrame");
286 parameters
.setWorkingDirectory(tempPath
);
287 if (stringDescriptorLocale
!= null && stringDescriptorLocale
.getDisplayName().length() > 0) {
288 parameters
.getVMParametersList().add("-Duser.language=" + stringDescriptorLocale
.getLanguage());
292 final RunProfile profile
= new MyRunProfile(module
, parameters
, UIDesignerBundle
.message("progress.preview.started", formFile
.getPresentableUrl()));
293 ProgramRunner defaultRunner
= RunnerRegistry
.getInstance().getRunner(DefaultRunExecutor
.EXECUTOR_ID
, profile
);
294 LOG
.assertTrue(defaultRunner
!= null);
295 defaultRunner
.execute(DefaultRunExecutor
.getRunExecutorInstance(), new ExecutionEnvironment(profile
, new DataContext() { // IDEADEV-3596
296 public Object
getData(String dataId
) {
297 if (PlatformDataKeys
.PROJECT
.is(dataId
)) {
298 return module
.getProject();
300 return dataContext
.getData(dataId
);
304 catch (ExecutionException e
) {
305 Messages
.showErrorDialog(
307 UIDesignerBundle
.message("error.cannot.preview.form", formFile
.getPath().replace('/', File
.separatorChar
), e
.getMessage()),
308 CommonBundle
.getErrorTitle()
313 private static final class MyRunProfile
implements ModuleRunProfile
{
314 private final Module myModule
;
315 private final JavaParameters myParams
;
316 private final String myStatusbarMessage
;
318 public MyRunProfile(final Module module
, final JavaParameters params
, final String statusbarMessage
) {
321 myStatusbarMessage
= statusbarMessage
;
324 public Icon
getIcon() {
328 public RunProfileState
getState(@NotNull final Executor executor
, @NotNull final ExecutionEnvironment env
) throws ExecutionException
{
329 final JavaCommandLineState state
= new JavaCommandLineState(env
) {
330 protected JavaParameters
createJavaParameters() {
334 public ExecutionResult
execute(@NotNull final Executor executor
, @NotNull final ProgramRunner runner
) throws ExecutionException
{
336 return super.execute(executor
, runner
);
339 final Project project
= myModule
.getProject();
340 SwingUtilities
.invokeLater(new Runnable() {
342 WindowManager
.getInstance().getStatusBar(project
).setInfo(myStatusbarMessage
);
348 state
.setConsoleBuilder(TextConsoleBuilderFactory
.getInstance().createBuilder(myModule
.getProject()));
352 public String
getName() {
353 return UIDesignerBundle
.message("title.form.preview");
356 public void checkConfiguration() throws RuntimeConfigurationException
{
360 public Module
[] getModules() {
361 return new Module
[] {myModule
};