Fixed bug: on loading ant even could overwrite previously loaded event which lead...
[fedora-idea.git] / plugins / ant / src / com / intellij / lang / ant / config / impl / AntConfigurationImpl.java
blobf7d78b85c679b72c2e8d68c965aeee0c679945b4
1 package com.intellij.lang.ant.config.impl;
3 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
4 import com.intellij.execution.configurations.ConfigurationType;
5 import com.intellij.execution.configurations.RunConfiguration;
6 import com.intellij.ide.DataAccessors;
7 import com.intellij.lang.ant.AntBundle;
8 import com.intellij.lang.ant.AntSupport;
9 import com.intellij.lang.ant.config.*;
10 import com.intellij.lang.ant.config.actions.TargetAction;
11 import com.intellij.lang.ant.psi.AntFile;
12 import com.intellij.lang.ant.psi.impl.AntFileImpl;
13 import com.intellij.openapi.actionSystem.AnAction;
14 import com.intellij.openapi.actionSystem.DataContext;
15 import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
16 import com.intellij.openapi.application.Application;
17 import com.intellij.openapi.application.ApplicationManager;
18 import com.intellij.openapi.application.ModalityState;
19 import com.intellij.openapi.components.PersistentStateComponent;
20 import com.intellij.openapi.components.State;
21 import com.intellij.openapi.components.Storage;
22 import com.intellij.openapi.components.StorageScheme;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.progress.ProgressIndicator;
25 import com.intellij.openapi.progress.ProgressManager;
26 import com.intellij.openapi.progress.Task;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.roots.ProjectRootManager;
29 import com.intellij.openapi.startup.StartupManager;
30 import com.intellij.openapi.util.*;
31 import com.intellij.openapi.vfs.VirtualFile;
32 import com.intellij.openapi.vfs.VirtualFileAdapter;
33 import com.intellij.openapi.vfs.VirtualFileEvent;
34 import com.intellij.openapi.vfs.VirtualFileManager;
35 import com.intellij.psi.PsiDocumentManager;
36 import com.intellij.psi.PsiElement;
37 import com.intellij.psi.PsiFile;
38 import com.intellij.psi.PsiManager;
39 import com.intellij.psi.xml.XmlFile;
40 import com.intellij.util.ActionRunner;
41 import com.intellij.util.EventDispatcher;
42 import com.intellij.util.StringSetSpinAllocator;
43 import com.intellij.util.concurrency.Semaphore;
44 import com.intellij.util.config.AbstractProperty;
45 import com.intellij.util.config.ValueProperty;
46 import com.intellij.util.containers.HashMap;
47 import org.jdom.Element;
48 import org.jetbrains.annotations.NonNls;
49 import org.jetbrains.annotations.NotNull;
50 import org.jetbrains.annotations.Nullable;
52 import javax.swing.*;
53 import java.util.*;
55 @State(
56 name = "AntConfiguration",
57 storages = {
58 @Storage(id = "default", file = "$PROJECT_FILE$"),
59 @Storage(id = "dir", file = "$PROJECT_CONFIG_DIR$/ant.xml", scheme = StorageScheme.DIRECTORY_BASED)
62 public class AntConfigurationImpl extends AntConfigurationBase implements PersistentStateComponent<Element>, ModificationTracker {
64 public static final ValueProperty<AntReference> DEFAULT_ANT = new ValueProperty<AntReference>("defaultAnt", AntReference.BUNDLED_ANT);
65 public static final ValueProperty<AntConfiguration> INSTANCE = new ValueProperty<AntConfiguration>("$instance", null);
66 public static final AbstractProperty<String> DEFAULT_JDK_NAME = new AbstractProperty<String>() {
67 public String getName() {
68 return "$defaultJDKName";
71 @Nullable
72 public String getDefault(final AbstractPropertyContainer container) {
73 return get(container);
76 @Nullable
77 public String get(final AbstractPropertyContainer container) {
78 if (!container.hasProperty(this)) return null;
79 AntConfiguration antConfiguration = AntConfigurationImpl.INSTANCE.get(container);
80 return ProjectRootManager.getInstance(antConfiguration.getProject()).getProjectJdkName();
83 public String copy(final String jdkName) {
84 return jdkName;
88 private static final Logger LOG = Logger.getInstance("#com.intellij.lang.ant.config.impl.AntConfigurationImpl");
89 @NonNls private static final String BUILD_FILE = "buildFile";
90 @NonNls private static final String CONTEXT_MAPPING = "contextMapping";
91 @NonNls private static final String CONTEXT = "context";
92 @NonNls private static final String URL = "url";
93 @NonNls private static final String EXECUTE_ON_ELEMENT = "executeOn";
94 @NonNls private static final String EVENT_ELEMENT = "event";
95 @NonNls private static final String TARGET_ELEMENT = "target";
97 private final PsiManager myPsiManager;
98 private final Map<ExecutionEvent, Pair<AntBuildFile, String>> myEventToTargetMap =
99 new HashMap<ExecutionEvent, Pair<AntBuildFile, String>>();
100 private final List<AntBuildFile> myBuildFiles = new ArrayList<AntBuildFile>();
101 private AntBuildFile[] myBuildFilesArray = null; // cached result of call to myBuildFiles.toArray()
102 private final Map<AntBuildFile, AntBuildModelBase> myModelToBuildFileMap = new HashMap<AntBuildFile, AntBuildModelBase>();
103 private final Map<VirtualFile, VirtualFile> myAntFileToContextFileMap = new java.util.HashMap<VirtualFile, VirtualFile>();
104 private final EventDispatcher<AntConfigurationListener> myEventDispatcher = EventDispatcher.create(AntConfigurationListener.class);
105 private final AntWorkspaceConfiguration myAntWorkspaceConfiguration;
106 private final StartupManager myStartupManager;
107 private boolean myInitializing;
108 private volatile long myModificationCount = 0;
110 public AntConfigurationImpl(final Project project, final AntWorkspaceConfiguration antWorkspaceConfiguration, final DaemonCodeAnalyzer daemon) {
111 super(project);
112 getProperties().registerProperty(DEFAULT_ANT, AntReference.EXTERNALIZER);
113 getProperties().rememberKey(INSTANCE);
114 getProperties().rememberKey(DEFAULT_JDK_NAME);
115 INSTANCE.set(getProperties(), this);
116 myAntWorkspaceConfiguration = antWorkspaceConfiguration;
117 myPsiManager = PsiManager.getInstance(project);
118 myStartupManager = StartupManager.getInstance(project);
119 addAntConfigurationListener(new AntConfigurationListener() {
120 public void configurationLoaded() {
121 restartDaemon();
123 public void buildFileChanged(final AntBuildFile buildFile) {
124 restartDaemon();
126 public void buildFileAdded(final AntBuildFile buildFile) {
127 restartDaemon();
129 public void buildFileRemoved(final AntBuildFile buildFile) {
130 restartDaemon();
132 private void restartDaemon() {
133 if (ApplicationManager.getApplication().isDispatchThread()) {
134 daemon.restart();
136 else {
137 SwingUtilities.invokeLater(new Runnable() {
138 public void run() {
139 daemon.restart();
145 VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() {
146 public void beforeFileDeletion(final VirtualFileEvent event) {
147 final VirtualFile vFile = event.getFile();
148 // cleanup
149 for (AntBuildFile file : getBuildFiles()) {
150 if (vFile.equals(file.getVirtualFile())) {
151 removeBuildFile(file);
152 break;
155 for (Iterator<Map.Entry<VirtualFile,VirtualFile>> it = myAntFileToContextFileMap.entrySet().iterator(); it.hasNext();) {
156 final Map.Entry<VirtualFile, VirtualFile> entry = it.next();
157 if (vFile.equals(entry.getKey()) || vFile.equals(entry.getValue())) {
158 it.remove();
162 }, project);
166 public Element getState() {
167 try {
168 final Element e = new Element("state");
169 writeExternal(e);
170 return e;
172 catch (WriteExternalException e1) {
173 LOG.error(e1);
174 return null;
178 public void loadState(Element state) {
179 try {
180 readExternal(state);
182 catch (InvalidDataException e) {
183 LOG.error(e);
187 private volatile Boolean myIsInitialized = null;
189 public boolean isInitialized() {
190 final Boolean initialized = myIsInitialized;
191 return initialized == null || initialized.booleanValue();
194 public AntBuildFile[] getBuildFiles() {
195 synchronized (myBuildFiles) {
196 if (myBuildFilesArray == null) {
197 myBuildFilesArray = myBuildFiles.toArray(new AntBuildFile[myBuildFiles.size()]);
199 return myBuildFilesArray;
203 public AntBuildFile addBuildFile(final VirtualFile file) throws AntNoFileException {
204 final AntBuildFile[] result = new AntBuildFile[]{null};
205 final AntNoFileException[] ex = new AntNoFileException[]{null};
206 final String title = AntBundle.message("register.ant.build.progress", file.getPresentableUrl());
207 ProgressManager.getInstance().run(new Task.Modal(getProject(), title, false) {
208 @Nullable
209 public NotificationInfo getNotificationInfo() {
210 return new NotificationInfo("Ant", "Ant Task Finished", "");
213 public void run(@NotNull final ProgressIndicator indicator) {
214 indicator.setIndeterminate(true);
215 indicator.pushState();
216 try {
217 indicator.setText(title);
218 myModificationCount++;
219 ApplicationManager.getApplication().runReadAction(new Runnable() {
220 public void run() {
221 try {
222 result[0] = addBuildFileImpl(file);
223 updateRegisteredActions();
225 catch (AntNoFileException e) {
226 ex[0] = e;
230 if (result[0] != null) {
231 ApplicationManager.getApplication().invokeLater(new Runnable() {
232 public void run() {
233 myEventDispatcher.getMulticaster().buildFileAdded(result[0]);
238 finally {
239 indicator.popState();
243 if (ex[0] != null) {
244 throw ex[0];
246 return result[0];
249 public void removeBuildFile(final AntBuildFile file) {
250 myModificationCount++;
251 removeBuildFileImpl(file);
252 updateRegisteredActions();
255 public void addAntConfigurationListener(final AntConfigurationListener listener) {
256 myEventDispatcher.addListener(listener);
259 public void removeAntConfigurationListener(final AntConfigurationListener listener) {
260 myEventDispatcher.removeListener(listener);
263 public boolean isFilterTargets() {
264 return myAntWorkspaceConfiguration.FILTER_TARGETS;
267 public void setFilterTargets(final boolean value) {
268 myAntWorkspaceConfiguration.FILTER_TARGETS = value;
271 public AntBuildTarget[] getMetaTargets(final AntBuildFile buildFile) {
272 final List<ExecutionEvent> events = getEventsByClass(ExecuteCompositeTargetEvent.class);
273 if (events.size() == 0) {
274 return AntBuildTargetBase.EMPTY_ARRAY;
276 final List<AntBuildTargetBase> targets = new ArrayList<AntBuildTargetBase>();
277 for (ExecutionEvent event : events) {
278 final MetaTarget target = (MetaTarget)getTargetForEvent(event);
279 if (target != null && buildFile.equals(target.getBuildFile())) {
280 targets.add(target);
283 return targets.toArray(new AntBuildTargetBase[targets.size()]);
286 public List<ExecutionEvent> getEventsForTarget(final AntBuildTarget target) {
287 final List<ExecutionEvent> list = new ArrayList<ExecutionEvent>();
288 synchronized (myEventToTargetMap) {
289 for (final ExecutionEvent event : myEventToTargetMap.keySet()) {
290 final AntBuildTarget targetForEvent = getTargetForEvent(event);
291 if (target.equals(targetForEvent)) {
292 list.add(event);
296 return list;
299 @Nullable
300 public AntBuildTarget getTargetForEvent(final ExecutionEvent event) {
301 final Pair<AntBuildFile, String> pair;
302 synchronized (myEventToTargetMap) {
303 pair = myEventToTargetMap.get(event);
305 if (pair == null) {
306 return null;
308 final AntBuildFileBase buildFile = (AntBuildFileBase)pair.first;
309 synchronized (myBuildFiles) {
310 if (!myBuildFiles.contains(buildFile)) {
311 return null; // file was removed
314 final String targetName = (String)pair.second;
316 final AntBuildTarget antBuildTarget = buildFile.getModel().findTarget(targetName);
317 if (antBuildTarget != null) {
318 return antBuildTarget;
320 final List<ExecutionEvent> events = getEventsByClass(ExecuteCompositeTargetEvent.class);
321 if (events.size() == 0) {
322 return null;
324 for (ExecutionEvent ev : events) {
325 final String presentableName = ev.getPresentableName();
326 if (Comparing.strEqual(targetName, presentableName)) {
327 return new MetaTarget(buildFile, presentableName, ((ExecuteCompositeTargetEvent)ev).getTargetNames());
330 return null;
333 public void setTargetForEvent(final AntBuildFile buildFile, final String targetName, final ExecutionEvent event) {
334 synchronized (myEventToTargetMap) {
335 myEventToTargetMap.put(event, new Pair<AntBuildFile, String>(buildFile, targetName));
339 public void clearTargetForEvent(final ExecutionEvent event) {
340 synchronized (myEventToTargetMap) {
341 myEventToTargetMap.remove(event);
345 public void updateBuildFile(final AntBuildFile buildFile) {
346 myModificationCount++;
347 myEventDispatcher.getMulticaster().buildFileChanged(buildFile);
348 updateRegisteredActions();
351 public boolean isAutoScrollToSource() {
352 return myAntWorkspaceConfiguration.IS_AUTOSCROLL_TO_SOURCE;
355 public void setAutoScrollToSource(final boolean value) {
356 myAntWorkspaceConfiguration.IS_AUTOSCROLL_TO_SOURCE = value;
359 public AntInstallation getProjectDefaultAnt() {
360 return DEFAULT_ANT.get(getProperties()).find(GlobalAntConfiguration.getInstance());
363 @Nullable
364 public AntBuildModel getModelIfRegistered(final AntBuildFile buildFile) {
365 synchronized (myBuildFiles) {
366 if (!myBuildFiles.contains(buildFile)) {
367 return null;
370 return getModel(buildFile);
373 public long getModificationCount() {
374 return myModificationCount;
377 private void readExternal(final Element parentNode) throws InvalidDataException {
378 myIsInitialized = Boolean.FALSE;
379 myAntWorkspaceConfiguration.loadFromProjectSettings(parentNode);
380 getProperties().readExternal(parentNode);
381 runWhenInitialized(new Runnable() {
382 public void run() {
383 loadBuildFileProjectProperties(parentNode);
388 private void runWhenInitialized(final Runnable runnable) {
389 if (getProject().isInitialized()) {
390 ApplicationManager.getApplication().runReadAction(new Runnable() {
391 public void run() {
392 runnable.run();
396 else {
397 myStartupManager.registerPostStartupActivity(new Runnable() {
398 public void run() {
399 runnable.run();
405 private void writeExternal(final Element parentNode) throws WriteExternalException {
406 getProperties().writeExternal(parentNode);
407 try {
408 ActionRunner.runInsideReadAction(new ActionRunner.InterruptibleRunnable() {
409 public void run() throws WriteExternalException {
410 for (final AntBuildFile buildFile : getBuildFiles()) {
411 final Element element = new Element(BUILD_FILE);
412 element.setAttribute(URL, buildFile.getVirtualFile().getUrl());
413 ((AntBuildFileBase)buildFile).writeProperties(element);
414 saveEvents(element, buildFile);
415 parentNode.addContent(element);
417 final List<VirtualFile> files = new ArrayList<VirtualFile>(myAntFileToContextFileMap.keySet());
418 // sort in order to minimize changes
419 Collections.sort(files, new Comparator<VirtualFile>() {
420 public int compare(final VirtualFile o1, final VirtualFile o2) {
421 return o1.getUrl().compareTo(o2.getUrl());
424 for (VirtualFile file : files) {
425 final Element element = new Element(CONTEXT_MAPPING);
426 final VirtualFile contextFile = myAntFileToContextFileMap.get(file);
427 element.setAttribute(URL, file.getUrl());
428 element.setAttribute(CONTEXT, contextFile.getUrl());
429 parentNode.addContent(element);
434 catch (WriteExternalException e) {
435 LOG.error(e);
436 throw e;
438 catch (RuntimeException e) {
439 LOG.error(e);
440 throw e;
442 catch (Exception e) {
443 LOG.error(e);
447 private void saveEvents(final Element element, final AntBuildFile buildFile) {
448 List<Element> events = null;
449 final Set<String> savedEvents = new HashSet<String>();
450 synchronized (myEventToTargetMap) {
451 for (final ExecutionEvent event : myEventToTargetMap.keySet()) {
452 final Pair<AntBuildFile, String> pair = myEventToTargetMap.get(event);
453 if (!buildFile.equals(pair.first)) {
454 continue;
456 Element eventElement = new Element(EXECUTE_ON_ELEMENT);
457 eventElement.setAttribute(EVENT_ELEMENT, event.getTypeId());
458 eventElement.setAttribute(TARGET_ELEMENT, pair.second);
460 final String id = event.writeExternal(eventElement, getProject());
461 if (savedEvents.contains(id)) continue;
462 savedEvents.add(id);
464 if (events == null) {
465 events = new ArrayList<Element>();
467 events.add(eventElement);
471 if (events != null) {
472 Collections.sort(events, EventElementComparator.INSTANCE);
473 for (Element eventElement : events) {
474 element.addContent(eventElement);
479 public AntBuildModel getModel(final AntBuildFile buildFile) {
480 AntBuildModelBase model = myModelToBuildFileMap.get(buildFile);
481 if (model == null) {
482 model = createModel(buildFile);
483 myModelToBuildFileMap.put(buildFile, model);
485 return model;
488 @Nullable
489 public AntBuildFile findBuildFileByActionId(final String id) {
490 for (AntBuildFile buildFile : getBuildFiles()) {
491 AntBuildModelBase model = (AntBuildModelBase)buildFile.getModel();
492 if (id.equals(model.getDefaultTargetActionId())) {
493 return buildFile;
495 if (model.hasTargetWithActionId(id)) return buildFile;
497 return null;
500 public boolean hasTasksToExecuteBeforeRun(final RunConfiguration configuration) {
501 return findExecuteBeforeRunEvent(configuration) != null;
504 public boolean executeTaskBeforeRun(final DataContext context, final RunConfiguration configuration) {
505 final ExecuteBeforeRunEvent foundEvent = findExecuteBeforeRunEvent(configuration);
506 return runTargetSynchronously(context, foundEvent);
509 public AntBuildTarget getTargetForBeforeRunEvent(RunConfiguration configuration) {
510 return getTargetForEvent(new ExecuteBeforeRunEvent(configuration));
513 public void setTargetForBeforeRunEvent(AntBuildFile buildFile,
514 String targetName,
515 RunConfiguration configuration) {
516 setTargetForEvent(buildFile, targetName, new ExecuteBeforeRunEvent(configuration));
519 private AntBuildModelBase createModel(final AntBuildFile buildFile) {
520 if (ApplicationManager.getApplication().isDispatchThread()) {
521 // otherwise commitAllDocuments() must have been called before the whole process was started
522 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
524 return new AntBuildModelImpl(buildFile);
527 private AntBuildFileBase addBuildFileImpl(final VirtualFile file) throws AntNoFileException {
528 PsiFile psiFile = myPsiManager.findFile(file);
529 if (psiFile == null) {
530 throw new AntNoFileException(AntBundle.message("cant.add.file.error.message"), file);
532 AntSupport.markFileAsAntFile(file, psiFile.getViewProvider(), true);
533 psiFile = AntSupport.getAntFile(psiFile);
534 if (psiFile == null) {
535 throw new AntNoFileException(AntBundle.message("cant.add.file.error.message"), file);
537 final AntFile antFile = (AntFile)psiFile;
538 final AntBuildFileImpl buildFile = new AntBuildFileImpl(antFile, this);
539 antFile.getSourceElement().putCopyableUserData(AntFileImpl.ANT_BUILD_FILE, buildFile);
540 synchronized (myBuildFiles) {
541 myBuildFilesArray = null;
542 myBuildFiles.add(buildFile);
544 return buildFile;
547 private void updateRegisteredActions() {
549 final List<Pair<String, AnAction>> actionList = new ArrayList<Pair<String, AnAction>>();
550 for (final AntBuildFile buildFile : getBuildFiles()) {
551 final AntBuildModelBase model = (AntBuildModelBase)buildFile.getModel();
552 String defaultTargetActionId = model.getDefaultTargetActionId();
553 if (defaultTargetActionId != null) {
554 final TargetAction action =
555 new TargetAction(buildFile, TargetAction.DEFAULT_TARGET_NAME, new String[]{TargetAction.DEFAULT_TARGET_NAME}, null);
556 actionList.add(new Pair<String, AnAction>(defaultTargetActionId, action));
559 collectTargetActions(model.getFilteredTargets(), actionList, buildFile);
560 collectTargetActions(getMetaTargets(buildFile), actionList, buildFile);
563 synchronized (this) {
564 // unregister Ant actions
565 ActionManagerEx actionManager = ActionManagerEx.getInstanceEx();
566 final String[] oldIds = actionManager.getActionIds(AntConfiguration.getActionIdPrefix(getProject()));
567 for (String oldId : oldIds) {
568 actionManager.unregisterAction(oldId);
570 final Set<String> registeredIds = StringSetSpinAllocator.alloc();
571 try {
572 for (Pair<String, AnAction> pair : actionList) {
573 if (!registeredIds.contains(pair.first)) {
574 registeredIds.add(pair.first);
575 actionManager.registerAction(pair.first, pair.second);
579 finally {
580 StringSetSpinAllocator.dispose(registeredIds);
585 private static void collectTargetActions(final AntBuildTarget[] targets,
586 final List<Pair<String, AnAction>> actionList,
587 final AntBuildFile buildFile) {
588 for (final AntBuildTarget target : targets) {
589 final String actionId = ((AntBuildTargetBase)target).getActionId();
590 if (actionId != null) {
591 final TargetAction action =
592 new TargetAction(buildFile, target.getName(), new String[]{target.getName()}, target.getNotEmptyDescription());
593 actionList.add(new Pair<String, AnAction>(actionId, action));
598 private void removeBuildFileImpl(AntBuildFile buildFile) {
599 final XmlFile xmlFile = ((AntFile)buildFile.getAntFile()).getSourceElement();
600 xmlFile.putCopyableUserData(AntFileImpl.ANT_BUILD_FILE, null);
601 AntSupport.markFileAsAntFile(xmlFile.getVirtualFile(), xmlFile.getViewProvider(), false);
602 synchronized (myBuildFiles) {
603 myBuildFilesArray = null;
604 myBuildFiles.remove(buildFile);
606 myModelToBuildFileMap.remove(buildFile);
607 myEventDispatcher.getMulticaster().buildFileRemoved(buildFile);
610 public boolean executeTargetBeforeCompile(final DataContext context) {
611 return runTargetSynchronously(context, ExecuteBeforeCompilationEvent.getInstance());
614 public boolean executeTargetAfterCompile(final DataContext context) {
615 return runTargetSynchronously(context, ExecuteAfterCompilationEvent.getInstance());
618 private boolean runTargetSynchronously(final DataContext dataContext, ExecutionEvent event) {
619 if (ApplicationManager.getApplication().isDispatchThread()) {
620 throw new IllegalStateException("Called in the event dispatch thread");
622 final AntBuildTarget target = getTargetForEvent(event);
623 if (target == null) {
624 // no task assigned
625 return true;
627 final Semaphore targetDone = new Semaphore();
628 final boolean[] result = new boolean[1];
629 try {
630 ApplicationManager.getApplication().invokeAndWait(new Runnable() {
632 public void run() {
633 Project project = DataAccessors.PROJECT.from(dataContext);
634 if (project == null || project.isDisposed()) {
635 result[0] = false;
636 return;
638 targetDone.down();
639 target.run(dataContext, new AntBuildListener() {
640 public void buildFinished(int state, int errorCount) {
641 result[0] = (state == AntBuildListener.FINISHED_SUCCESSFULLY) && (errorCount == 0);
642 targetDone.up();
646 }, ModalityState.NON_MODAL);
648 catch (Exception e) {
649 LOG.error(e);
650 return false;
652 targetDone.waitFor();
653 return result[0];
656 private List<ExecutionEvent> getEventsByClass(Class eventClass) {
657 if (!myInitializing) ensureInitialized();
658 final List<ExecutionEvent> list = new ArrayList<ExecutionEvent>();
659 synchronized (myEventToTargetMap) {
660 for (final ExecutionEvent event : myEventToTargetMap.keySet()) {
661 if (eventClass.isInstance(event)) {
662 list.add(event);
666 return list;
669 private void loadBuildFileProjectProperties(final Element parentNode) {
670 final List<Pair<Element, VirtualFile>> files = new ArrayList<Pair<Element, VirtualFile>>();
671 final VirtualFileManager vfManager = VirtualFileManager.getInstance();
672 for (final Object o : parentNode.getChildren(BUILD_FILE)) {
673 final Element element = (Element)o;
674 final String url = element.getAttributeValue(URL);
675 final VirtualFile file = vfManager.findFileByUrl(url);
676 if (file != null) {
677 files.add(new Pair<Element, VirtualFile>(element, file));
681 // contexts
682 myAntFileToContextFileMap.clear();
683 for (final Object o : parentNode.getChildren(CONTEXT_MAPPING)) {
684 final Element element = (Element)o;
685 final String url = element.getAttributeValue(URL);
686 final String contextUrl = element.getAttributeValue(CONTEXT);
687 final VirtualFile file = vfManager.findFileByUrl(url);
688 final VirtualFile contextFile = vfManager.findFileByUrl(contextUrl);
689 if (file != null && contextFile != null) {
690 myAntFileToContextFileMap.put(file, contextFile);
694 final String title = AntBundle.message("loading.ant.config.progress");
695 queueLater(new Task.Backgroundable(getProject(), title, false) {
696 public void run(@NotNull final ProgressIndicator indicator) {
697 indicator.setIndeterminate(true);
698 indicator.pushState();
699 try {
700 indicator.setText(title);
701 ApplicationManager.getApplication().runReadAction(new Runnable() {
702 public void run() {
703 try {
704 myInitializing = true;
705 // first, remove existing files
706 final AntBuildFile[] currentFiles = getBuildFiles();
707 for (AntBuildFile file : currentFiles) {
708 removeBuildFile(file);
710 // then fill the configuration with the files configured in xml
711 List<Pair<Element, AntBuildFileBase>> buildFiles = new ArrayList<Pair<Element, AntBuildFileBase>>(files.size());
712 for (Pair<Element, VirtualFile> pair : files) {
713 final Element element = pair.getFirst();
714 final VirtualFile file = pair.getSecond();
715 try {
716 final AntBuildFileBase buildFile = addBuildFileImpl(file);
717 buildFile.readProperties(element);
718 buildFiles.add(new Pair<Element, AntBuildFileBase>(element, buildFile));
720 catch (AntNoFileException ignored) {
722 catch (InvalidDataException e) {
723 LOG.error(e);
726 // updating properties separately to avoid unnecesary building of PSI after clearing caches
727 for (Pair<Element, AntBuildFileBase> pair : buildFiles) {
728 final AntBuildFileBase buildFile = pair.getSecond();
729 buildFile.updateProperties();
730 for (final Object o1 : pair.getFirst().getChildren(EXECUTE_ON_ELEMENT)) {
731 Element e = (Element)o1;
732 String eventId = e.getAttributeValue(EVENT_ELEMENT);
733 ExecutionEvent event = null;
734 String targetName = e.getAttributeValue(TARGET_ELEMENT);
735 if (ExecuteBeforeCompilationEvent.TYPE_ID.equals(eventId)) {
736 event = ExecuteBeforeCompilationEvent.getInstance();
738 else if (ExecuteAfterCompilationEvent.TYPE_ID.equals(eventId)) {
739 event = ExecuteAfterCompilationEvent.getInstance();
741 else if (ExecuteBeforeRunEvent.TYPE_ID.equals(eventId)) {
742 event = new ExecuteBeforeRunEvent();
744 else if (ExecuteCompositeTargetEvent.TYPE_ID.equals(eventId)) {
745 try {
746 event = new ExecuteCompositeTargetEvent(targetName);
748 catch (WrongNameFormatException e1) {
749 LOG.info(e1);
750 event = null;
753 if (event != null) {
754 try {
755 event.readExternal(e, getProject());
756 setTargetForEvent(buildFile, targetName, event);
758 catch (InvalidDataException readFailed) {
759 LOG.info(readFailed.getMessage());
764 AntWorkspaceConfiguration.getInstance(getProject()).loadFileProperties();
766 catch (InvalidDataException e) {
767 LOG.error(e);
769 finally {
770 updateRegisteredActions();
771 myInitializing = false;
772 myIsInitialized = Boolean.TRUE;
773 ApplicationManager.getApplication().invokeLater(new Runnable() {
774 public void run() {
775 myEventDispatcher.getMulticaster().configurationLoaded();
782 finally {
783 indicator.popState();
789 private static void queueLater(final Task task) {
790 final Application app = ApplicationManager.getApplication();
791 if (app.isDispatchThread()) {
792 task.queue();
793 } else {
794 app.invokeLater(new Runnable() {
795 public void run() {
796 task.queue();
802 public void setContextFile(@NotNull AntFile file, @Nullable AntFile context) {
803 if (context != null) {
804 myAntFileToContextFileMap.put(file.getVirtualFile(), context.getVirtualFile());
806 else {
807 myAntFileToContextFileMap.remove(file.getVirtualFile());
809 file.clearCaches();
812 public AntFile getContextFile(@Nullable final AntFile file) {
813 return file != null? AntSupport.toAntFile(myAntFileToContextFileMap.get(file.getVirtualFile()), getProject()) : null;
816 @Nullable
817 public AntFile getEffectiveContextFile(final AntFile file) {
818 return new Object() {
819 @Nullable
820 AntFile findContext(final AntFile file, Set<PsiElement> processed) {
821 if (file != null) {
822 processed.add(file);
823 final AntFile contextFile = AntSupport.toAntFile(myAntFileToContextFileMap.get(file.getVirtualFile()), getProject());
824 return (contextFile == null || processed.contains(contextFile))? file : findContext(contextFile, processed);
826 return null;
828 }.findContext(file, new HashSet<PsiElement>());
831 @Nullable
832 ExecuteBeforeRunEvent findExecuteBeforeRunEvent(RunConfiguration configuration) {
833 final ConfigurationType type = configuration.getType();
834 for (final ExecutionEvent e : getEventsByClass(ExecuteBeforeRunEvent.class)) {
835 final ExecuteBeforeRunEvent event = (ExecuteBeforeRunEvent)e;
836 if (event.isFor(type) || event.isFor(configuration)) return event;
838 return null;
841 private static class EventElementComparator implements Comparator<Element> {
842 static final Comparator<? super Element> INSTANCE = new EventElementComparator();
844 private static final String[] COMPARABLE_ATTRIB_NAMES = new String[] {
845 EVENT_ELEMENT,
846 TARGET_ELEMENT,
847 ExecuteBeforeRunEvent.RUN_CONFIGURATION_TYPE_ATTR,
848 ExecuteBeforeRunEvent.RUN_CONFIUGRATION_NAME_ATTR,
849 ExecuteCompositeTargetEvent.PRESENTABLE_NAME
852 public int compare(final Element o1, final Element o2) {
853 for (String attribName : COMPARABLE_ATTRIB_NAMES) {
854 final int valuesEqual = Comparing.compare(o1.getAttributeValue(attribName), o2.getAttributeValue(attribName));
855 if (valuesEqual != 0) {
856 return valuesEqual;
859 return 0;