blob: 5cf28c8f42f8502dce5a24838f5b67a026f0309f [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.lang.ant.config.impl;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.execution.RunManagerEx;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.impl.RunManagerImpl;
import com.intellij.lang.ant.AntBundle;
import com.intellij.lang.ant.AntSupport;
import com.intellij.lang.ant.config.*;
import com.intellij.lang.ant.config.actions.TargetAction;
import com.intellij.lang.ant.dom.AntDomFileDescription;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileAdapter;
import com.intellij.openapi.vfs.VirtualFileEvent;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.ActionRunner;
import com.intellij.util.EventDispatcher;
import com.intellij.util.StringSetSpinAllocator;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.config.AbstractProperty;
import com.intellij.util.config.ValueProperty;
import com.intellij.util.containers.HashMap;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
@State(
name = "AntConfiguration",
storages = {
@Storage(file = StoragePathMacros.PROJECT_FILE),
@Storage(file = StoragePathMacros.PROJECT_CONFIG_DIR + "/ant.xml", scheme = StorageScheme.DIRECTORY_BASED)
}
)
public class AntConfigurationImpl extends AntConfigurationBase implements PersistentStateComponent<Element>, ModificationTracker {
public static final ValueProperty<AntReference> DEFAULT_ANT = new ValueProperty<AntReference>("defaultAnt", AntReference.BUNDLED_ANT);
public static final ValueProperty<AntConfiguration> INSTANCE = new ValueProperty<AntConfiguration>("$instance", null);
public static final AbstractProperty<String> DEFAULT_JDK_NAME = new AbstractProperty<String>() {
public String getName() {
return "$defaultJDKName";
}
@Nullable
public String getDefault(final AbstractPropertyContainer container) {
return get(container);
}
@Nullable
public String get(final AbstractPropertyContainer container) {
if (!container.hasProperty(this)) return null;
AntConfiguration antConfiguration = AntConfigurationImpl.INSTANCE.get(container);
return ProjectRootManager.getInstance(antConfiguration.getProject()).getProjectSdkName();
}
public String copy(final String jdkName) {
return jdkName;
}
};
private static final Logger LOG = Logger.getInstance("#com.intellij.lang.ant.config.impl.AntConfigurationImpl");
@NonNls private static final String BUILD_FILE = "buildFile";
@NonNls private static final String CONTEXT_MAPPING = "contextMapping";
@NonNls private static final String CONTEXT = "context";
@NonNls private static final String URL = "url";
@NonNls private static final String EXECUTE_ON_ELEMENT = "executeOn";
@NonNls private static final String EVENT_ELEMENT = "event";
@NonNls private static final String TARGET_ELEMENT = "target";
private final PsiManager myPsiManager;
private final Map<ExecutionEvent, Pair<AntBuildFile, String>> myEventToTargetMap =
new HashMap<ExecutionEvent, Pair<AntBuildFile, String>>();
private final List<AntBuildFileBase> myBuildFiles = new ArrayList<AntBuildFileBase>();
private volatile AntBuildFileBase[] myBuildFilesArray = null; // cached result of call to myBuildFiles.toArray()
private final Map<AntBuildFile, AntBuildModelBase> myModelToBuildFileMap = new HashMap<AntBuildFile, AntBuildModelBase>();
private final Map<VirtualFile, VirtualFile> myAntFileToContextFileMap = new java.util.HashMap<VirtualFile, VirtualFile>();
private final EventDispatcher<AntConfigurationListener> myEventDispatcher = EventDispatcher.create(AntConfigurationListener.class);
private final AntWorkspaceConfiguration myAntWorkspaceConfiguration;
private final StartupManager myStartupManager;
private boolean myInitializing;
private volatile long myModificationCount = 0;
public AntConfigurationImpl(final Project project, final AntWorkspaceConfiguration antWorkspaceConfiguration, final DaemonCodeAnalyzer daemon) {
super(project);
getProperties().registerProperty(DEFAULT_ANT, AntReference.EXTERNALIZER);
getProperties().rememberKey(INSTANCE);
getProperties().rememberKey(DEFAULT_JDK_NAME);
INSTANCE.set(getProperties(), this);
myAntWorkspaceConfiguration = antWorkspaceConfiguration;
myPsiManager = PsiManager.getInstance(project);
myStartupManager = StartupManager.getInstance(project);
addAntConfigurationListener(new AntConfigurationListener() {
public void configurationLoaded() {
restartDaemon();
}
public void buildFileChanged(final AntBuildFile buildFile) {
restartDaemon();
}
public void buildFileAdded(final AntBuildFile buildFile) {
restartDaemon();
}
public void buildFileRemoved(final AntBuildFile buildFile) {
restartDaemon();
}
private void restartDaemon() {
if (ApplicationManager.getApplication().isDispatchThread()) {
daemon.restart();
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
daemon.restart();
}
});
}
}
});
VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() {
public void beforeFileDeletion(final VirtualFileEvent event) {
final VirtualFile vFile = event.getFile();
// cleanup
for (AntBuildFile file : getBuildFiles()) {
if (vFile.equals(file.getVirtualFile())) {
removeBuildFile(file);
break;
}
}
for (Iterator<Map.Entry<VirtualFile,VirtualFile>> it = myAntFileToContextFileMap.entrySet().iterator(); it.hasNext();) {
final Map.Entry<VirtualFile, VirtualFile> entry = it.next();
if (vFile.equals(entry.getKey()) || vFile.equals(entry.getValue())) {
it.remove();
}
}
}
}, project);
}
public Element getState() {
try {
final Element e = new Element("state");
writeExternal(e);
return e;
}
catch (WriteExternalException e1) {
LOG.error(e1);
return null;
}
}
public void loadState(Element state) {
try {
readExternal(state);
}
catch (InvalidDataException e) {
LOG.error(e);
}
}
private volatile Boolean myIsInitialized = null;
public boolean isInitialized() {
final Boolean initialized = myIsInitialized;
return initialized == null || initialized.booleanValue();
}
public AntBuildFileBase[] getBuildFiles() {
AntBuildFileBase[] result = myBuildFilesArray;
if (result == null) {
synchronized (myBuildFiles) {
result = myBuildFilesArray;
if (result == null) {
myBuildFilesArray = result = myBuildFiles.toArray(new AntBuildFileBase[myBuildFiles.size()]);
}
}
}
return result;
}
public AntBuildFile addBuildFile(final VirtualFile file) throws AntNoFileException {
final AntBuildFile[] result = new AntBuildFile[]{null};
final AntNoFileException[] ex = new AntNoFileException[]{null};
final String title = AntBundle.message("register.ant.build.progress", file.getPresentableUrl());
ProgressManager.getInstance().run(new Task.Modal(getProject(), title, false) {
@Nullable
public NotificationInfo getNotificationInfo() {
return new NotificationInfo("Ant", "Ant Task Finished", "");
}
public void run(@NotNull final ProgressIndicator indicator) {
indicator.setIndeterminate(true);
indicator.pushState();
try {
indicator.setText(title);
myModificationCount++;
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
try {
result[0] = addBuildFileImpl(file);
updateRegisteredActions();
}
catch (AntNoFileException e) {
ex[0] = e;
}
}
});
if (result[0] != null) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
myEventDispatcher.getMulticaster().buildFileAdded(result[0]);
}
});
}
}
finally {
indicator.popState();
}
}
});
if (ex[0] != null) {
throw ex[0];
}
return result[0];
}
public void removeBuildFile(final AntBuildFile file) {
myModificationCount++;
removeBuildFileImpl(file);
updateRegisteredActions();
}
public void addAntConfigurationListener(final AntConfigurationListener listener) {
myEventDispatcher.addListener(listener);
}
public void removeAntConfigurationListener(final AntConfigurationListener listener) {
myEventDispatcher.removeListener(listener);
}
public boolean isFilterTargets() {
return myAntWorkspaceConfiguration.FILTER_TARGETS;
}
public void setFilterTargets(final boolean value) {
myAntWorkspaceConfiguration.FILTER_TARGETS = value;
}
public AntBuildTarget[] getMetaTargets(final AntBuildFile buildFile) {
final List<ExecutionEvent> events = getEventsByClass(ExecuteCompositeTargetEvent.class);
if (events.size() == 0) {
return AntBuildTargetBase.EMPTY_ARRAY;
}
final List<AntBuildTargetBase> targets = new ArrayList<AntBuildTargetBase>();
for (ExecutionEvent event : events) {
final MetaTarget target = (MetaTarget)getTargetForEvent(event);
if (target != null && buildFile.equals(target.getBuildFile())) {
targets.add(target);
}
}
return targets.toArray(new AntBuildTargetBase[targets.size()]);
}
public List<ExecutionEvent> getEventsForTarget(final AntBuildTarget target) {
final List<ExecutionEvent> list = new ArrayList<ExecutionEvent>();
synchronized (myEventToTargetMap) {
for (final ExecutionEvent event : myEventToTargetMap.keySet()) {
final AntBuildTarget targetForEvent = getTargetForEvent(event);
if (target.equals(targetForEvent)) {
list.add(event);
}
}
}
return list;
}
@Nullable
public AntBuildTarget getTargetForEvent(final ExecutionEvent event) {
final Pair<AntBuildFile, String> pair;
synchronized (myEventToTargetMap) {
pair = myEventToTargetMap.get(event);
}
if (pair == null) {
return null;
}
final AntBuildFileBase buildFile = (AntBuildFileBase)pair.first;
synchronized (myBuildFiles) {
if (!myBuildFiles.contains(buildFile)) {
return null; // file was removed
}
}
final String targetName = pair.second;
final AntBuildTarget antBuildTarget = buildFile.getModel().findTarget(targetName);
if (antBuildTarget != null) {
return antBuildTarget;
}
final List<ExecutionEvent> events = getEventsByClass(ExecuteCompositeTargetEvent.class);
if (events.size() == 0) {
return null;
}
for (ExecutionEvent ev : events) {
final String name = ExecuteCompositeTargetEvent.TYPE_ID.equals(ev.getTypeId())? ((ExecuteCompositeTargetEvent)ev).getMetaTargetName() : ev.getPresentableName();
if (Comparing.strEqual(targetName, name)) {
return new MetaTarget(buildFile, ev.getPresentableName(), ((ExecuteCompositeTargetEvent)ev).getTargetNames());
}
}
return null;
}
public void setTargetForEvent(final AntBuildFile buildFile, final String targetName, final ExecutionEvent event) {
synchronized (myEventToTargetMap) {
myEventToTargetMap.put(event, new Pair<AntBuildFile, String>(buildFile, targetName));
}
}
public void clearTargetForEvent(final ExecutionEvent event) {
synchronized (myEventToTargetMap) {
myEventToTargetMap.remove(event);
}
}
public void handleTargetRename(String oldTargetName, String newTargetName) {
synchronized (myEventToTargetMap) {
for (Map.Entry<ExecutionEvent, Pair<AntBuildFile, String>> entry : myEventToTargetMap.entrySet()) {
final Pair<AntBuildFile, String> pair = entry.getValue();
if (pair != null && Comparing.equal(pair.getSecond(), oldTargetName)) {
entry.setValue(new Pair<AntBuildFile, String>(pair.getFirst(), newTargetName));
}
}
}
}
public void updateBuildFile(final AntBuildFile buildFile) {
myModificationCount++;
myEventDispatcher.getMulticaster().buildFileChanged(buildFile);
updateRegisteredActions();
}
public boolean isAutoScrollToSource() {
return myAntWorkspaceConfiguration.IS_AUTOSCROLL_TO_SOURCE;
}
public void setAutoScrollToSource(final boolean value) {
myAntWorkspaceConfiguration.IS_AUTOSCROLL_TO_SOURCE = value;
}
public AntInstallation getProjectDefaultAnt() {
return DEFAULT_ANT.get(getProperties()).find(GlobalAntConfiguration.getInstance());
}
@Nullable
public AntBuildModel getModelIfRegistered(final AntBuildFile buildFile) {
synchronized (myBuildFiles) {
if (!myBuildFiles.contains(buildFile)) {
return null;
}
}
return getModel(buildFile);
}
public long getModificationCount() {
return myModificationCount;
}
private void readExternal(final Element parentNode) throws InvalidDataException {
myIsInitialized = Boolean.FALSE;
myAntWorkspaceConfiguration.loadFromProjectSettings(parentNode);
getProperties().readExternal(parentNode);
runWhenInitialized(new Runnable() {
public void run() {
loadBuildFileProjectProperties(parentNode);
}
});
}
private void runWhenInitialized(final Runnable runnable) {
if (getProject().isInitialized()) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
runnable.run();
}
});
}
else {
myStartupManager.runWhenProjectIsInitialized(new Runnable() {
public void run() {
runnable.run();
}
});
}
}
private void writeExternal(final Element parentNode) throws WriteExternalException {
getProperties().writeExternal(parentNode);
try {
ActionRunner.runInsideReadAction(new ActionRunner.InterruptibleRunnable() {
public void run() throws WriteExternalException {
for (final AntBuildFileBase buildFile : getBuildFiles()) {
final Element element = new Element(BUILD_FILE);
element.setAttribute(URL, buildFile.getVirtualFile().getUrl());
buildFile.writeProperties(element);
saveEvents(element, buildFile);
parentNode.addContent(element);
}
final List<VirtualFile> files = new ArrayList<VirtualFile>(myAntFileToContextFileMap.keySet());
// sort in order to minimize changes
Collections.sort(files, new Comparator<VirtualFile>() {
public int compare(final VirtualFile o1, final VirtualFile o2) {
return o1.getUrl().compareTo(o2.getUrl());
}
});
for (VirtualFile file : files) {
final Element element = new Element(CONTEXT_MAPPING);
final VirtualFile contextFile = myAntFileToContextFileMap.get(file);
element.setAttribute(URL, file.getUrl());
element.setAttribute(CONTEXT, contextFile.getUrl());
parentNode.addContent(element);
}
}
});
}
catch (WriteExternalException e) {
LOG.error(e);
throw e;
}
catch (RuntimeException e) {
LOG.error(e);
throw e;
}
catch (Exception e) {
LOG.error(e);
}
}
private void saveEvents(final Element element, final AntBuildFile buildFile) {
List<Element> events = null;
final Set<String> savedEvents = new HashSet<String>();
synchronized (myEventToTargetMap) {
for (final ExecutionEvent event : myEventToTargetMap.keySet()) {
final Pair<AntBuildFile, String> pair = myEventToTargetMap.get(event);
if (!buildFile.equals(pair.first)) {
continue;
}
Element eventElement = new Element(EXECUTE_ON_ELEMENT);
eventElement.setAttribute(EVENT_ELEMENT, event.getTypeId());
eventElement.setAttribute(TARGET_ELEMENT, pair.second);
final String id = event.writeExternal(eventElement, getProject());
if (savedEvents.contains(id)) continue;
savedEvents.add(id);
if (events == null) {
events = new ArrayList<Element>();
}
events.add(eventElement);
}
}
if (events != null) {
Collections.sort(events, EventElementComparator.INSTANCE);
for (Element eventElement : events) {
element.addContent(eventElement);
}
}
}
public AntBuildModel getModel(final AntBuildFile buildFile) {
AntBuildModelBase model = myModelToBuildFileMap.get(buildFile);
if (model == null) {
model = createModel(buildFile);
myModelToBuildFileMap.put(buildFile, model);
}
return model;
}
@Nullable
public AntBuildFile findBuildFileByActionId(final String id) {
for (AntBuildFile buildFile : getBuildFiles()) {
AntBuildModelBase model = (AntBuildModelBase)buildFile.getModel();
if (id.equals(model.getDefaultTargetActionId())) {
return buildFile;
}
if (model.hasTargetWithActionId(id)) return buildFile;
}
return null;
}
private AntBuildModelBase createModel(final AntBuildFile buildFile) {
if (ApplicationManager.getApplication().isDispatchThread()) {
// otherwise commitAllDocuments() must have been called before the whole process was started
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
}
return new AntBuildModelImpl(buildFile);
}
private AntBuildFileBase addBuildFileImpl(final VirtualFile file) throws AntNoFileException {
PsiFile xmlFile = myPsiManager.findFile(file);
if (!(xmlFile instanceof XmlFile)) {
throw new AntNoFileException("the file is not an xml file", file);
}
AntSupport.markFileAsAntFile(file, xmlFile.getProject(), true);
if (!AntDomFileDescription.isAntFile(((XmlFile)xmlFile))) {
throw new AntNoFileException("the file is not recognized as an ANT file", file);
}
final AntBuildFileImpl buildFile = new AntBuildFileImpl((XmlFile)xmlFile, this);
synchronized (myBuildFiles) {
myBuildFilesArray = null;
myBuildFiles.add(buildFile);
}
return buildFile;
}
private void updateRegisteredActions() {
final Project project = getProject();
if (project.isDisposed()) {
return;
}
final List<Pair<String, AnAction>> actionList = new ArrayList<Pair<String, AnAction>>();
for (final AntBuildFile buildFile : getBuildFiles()) {
final AntBuildModelBase model = (AntBuildModelBase)buildFile.getModel();
String defaultTargetActionId = model.getDefaultTargetActionId();
if (defaultTargetActionId != null) {
final TargetAction action =
new TargetAction(buildFile, TargetAction.DEFAULT_TARGET_NAME, new String[]{TargetAction.DEFAULT_TARGET_NAME}, null);
actionList.add(new Pair<String, AnAction>(defaultTargetActionId, action));
}
collectTargetActions(model.getFilteredTargets(), actionList, buildFile);
collectTargetActions(getMetaTargets(buildFile), actionList, buildFile);
}
synchronized (this) {
// unregister Ant actions
ActionManagerEx actionManager = ActionManagerEx.getInstanceEx();
final String[] oldIds = actionManager.getActionIds(AntConfiguration.getActionIdPrefix(project));
for (String oldId : oldIds) {
actionManager.unregisterAction(oldId);
}
final Set<String> registeredIds = StringSetSpinAllocator.alloc();
try {
for (Pair<String, AnAction> pair : actionList) {
if (!registeredIds.contains(pair.first)) {
registeredIds.add(pair.first);
actionManager.registerAction(pair.first, pair.second);
}
}
}
finally {
StringSetSpinAllocator.dispose(registeredIds);
}
}
}
private static void collectTargetActions(final AntBuildTarget[] targets,
final List<Pair<String, AnAction>> actionList,
final AntBuildFile buildFile) {
for (final AntBuildTarget target : targets) {
final String actionId = ((AntBuildTargetBase)target).getActionId();
if (actionId != null) {
final TargetAction action =
new TargetAction(buildFile, target.getName(), new String[]{target.getName()}, target.getNotEmptyDescription());
actionList.add(new Pair<String, AnAction>(actionId, action));
}
}
}
private void removeBuildFileImpl(AntBuildFile buildFile) {
final XmlFile antFile = buildFile.getAntFile();
if (antFile != null) {
AntSupport.markFileAsAntFile(antFile.getOriginalFile().getVirtualFile(), antFile.getProject(), false);
}
synchronized (myBuildFiles) {
myBuildFilesArray = null;
myBuildFiles.remove(buildFile);
}
myModelToBuildFileMap.remove(buildFile);
myEventDispatcher.getMulticaster().buildFileRemoved(buildFile);
}
public boolean executeTargetBeforeCompile(final DataContext context) {
return runTargetSynchronously(context, ExecuteBeforeCompilationEvent.getInstance());
}
public boolean executeTargetAfterCompile(final DataContext context) {
return runTargetSynchronously(context, ExecuteAfterCompilationEvent.getInstance());
}
private boolean runTargetSynchronously(final DataContext dataContext, ExecutionEvent event) {
if (ApplicationManager.getApplication().isDispatchThread()) {
throw new IllegalStateException("Called in the event dispatch thread");
}
final AntBuildTarget target = getTargetForEvent(event);
if (target == null) {
// no task assigned
return true;
}
return executeTargetSynchronously(dataContext, target);
}
public static boolean executeTargetSynchronously(final DataContext dataContext, final AntBuildTarget target) {
return executeTargetSynchronously(dataContext, target, Collections.<BuildFileProperty>emptyList());
}
public static boolean executeTargetSynchronously(final DataContext dataContext, final AntBuildTarget target, final List<BuildFileProperty> additionalProperties) {
final Semaphore targetDone = new Semaphore();
final boolean[] result = new boolean[1];
try {
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
public void run() {
Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project == null || project.isDisposed()) {
result[0] = false;
return;
}
targetDone.down();
target.run(dataContext, additionalProperties, new AntBuildListener() {
public void buildFinished(int state, int errorCount) {
result[0] = (state == AntBuildListener.FINISHED_SUCCESSFULLY) && (errorCount == 0);
targetDone.up();
}
});
}
}, ModalityState.NON_MODAL);
}
catch (Exception e) {
LOG.error(e);
return false;
}
targetDone.waitFor();
return result[0];
}
private List<ExecutionEvent> getEventsByClass(Class eventClass) {
if (!myInitializing) ensureInitialized();
final List<ExecutionEvent> list = new ArrayList<ExecutionEvent>();
synchronized (myEventToTargetMap) {
for (final ExecutionEvent event : myEventToTargetMap.keySet()) {
if (eventClass.isInstance(event)) {
list.add(event);
}
}
}
return list;
}
private void loadBuildFileProjectProperties(final Element parentNode) {
final List<Pair<Element, VirtualFile>> files = new ArrayList<Pair<Element, VirtualFile>>();
final VirtualFileManager vfManager = VirtualFileManager.getInstance();
for (final Object o : parentNode.getChildren(BUILD_FILE)) {
final Element element = (Element)o;
final String url = element.getAttributeValue(URL);
final VirtualFile file = vfManager.findFileByUrl(url);
if (file != null) {
files.add(new Pair<Element, VirtualFile>(element, file));
}
}
// contexts
myAntFileToContextFileMap.clear();
for (final Object o : parentNode.getChildren(CONTEXT_MAPPING)) {
final Element element = (Element)o;
final String url = element.getAttributeValue(URL);
final String contextUrl = element.getAttributeValue(CONTEXT);
final VirtualFile file = vfManager.findFileByUrl(url);
final VirtualFile contextFile = vfManager.findFileByUrl(contextUrl);
if (file != null && contextFile != null) {
myAntFileToContextFileMap.put(file, contextFile);
}
}
final String title = AntBundle.message("loading.ant.config.progress");
queueLater(new Task.Backgroundable(getProject(), title, false) {
public void run(@NotNull final ProgressIndicator indicator) {
if (getProject().isDisposed()) {
return;
}
indicator.setIndeterminate(true);
indicator.pushState();
try {
indicator.setText(title);
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
try {
myInitializing = true;
// first, remove existing files
final AntBuildFile[] currentFiles = getBuildFiles();
for (AntBuildFile file : currentFiles) {
removeBuildFile(file);
}
// then fill the configuration with the files configured in xml
List<Pair<Element, AntBuildFileBase>> buildFiles = new ArrayList<Pair<Element, AntBuildFileBase>>(files.size());
for (Pair<Element, VirtualFile> pair : files) {
final Element element = pair.getFirst();
final VirtualFile file = pair.getSecond();
try {
final AntBuildFileBase buildFile = addBuildFileImpl(file);
buildFile.readProperties(element);
buildFiles.add(new Pair<Element, AntBuildFileBase>(element, buildFile));
}
catch (AntNoFileException ignored) {
}
catch (InvalidDataException e) {
LOG.error(e);
}
}
// updating properties separately to avoid unnecesary building of PSI after clearing caches
for (Pair<Element, AntBuildFileBase> pair : buildFiles) {
final AntBuildFileBase buildFile = pair.getSecond();
buildFile.updateProperties();
final VirtualFile vFile = buildFile.getVirtualFile();
final String buildFileUrl = vFile != null? vFile.getUrl() : null;
for (final Object o1 : pair.getFirst().getChildren(EXECUTE_ON_ELEMENT)) {
final Element e = (Element)o1;
final String eventId = e.getAttributeValue(EVENT_ELEMENT);
ExecutionEvent event = null;
final String targetName = e.getAttributeValue(TARGET_ELEMENT);
if (ExecuteBeforeCompilationEvent.TYPE_ID.equals(eventId)) {
event = ExecuteBeforeCompilationEvent.getInstance();
}
else if (ExecuteAfterCompilationEvent.TYPE_ID.equals(eventId)) {
event = ExecuteAfterCompilationEvent.getInstance();
}
else if ("beforeRun".equals(eventId)) {
/*
for compatibility with previous format
<buildFile url="file://$PROJECT_DIR$/module/src/support-scripts.xml">
<executeOn event="beforeRun" target="prebuild-steps" runConfigurationType="Application" runConfigurationName="Main" />
</buildFile>
*/
final String configType = e.getAttributeValue("runConfigurationType");
final String configName = e.getAttributeValue("runConfigurationName");
convertToBeforeRunTask(myProject, buildFileUrl, targetName, configType, configName);
}
else if (ExecuteCompositeTargetEvent.TYPE_ID.equals(eventId)) {
try {
event = new ExecuteCompositeTargetEvent(targetName);
}
catch (WrongNameFormatException e1) {
LOG.info(e1);
event = null;
}
}
if (event != null) {
try {
event.readExternal(e, getProject());
setTargetForEvent(buildFile, targetName, event);
}
catch (InvalidDataException readFailed) {
LOG.info(readFailed.getMessage());
}
}
}
}
AntWorkspaceConfiguration.getInstance(getProject()).loadFileProperties();
}
catch (InvalidDataException e) {
LOG.error(e);
}
finally {
updateRegisteredActions();
myInitializing = false;
myIsInitialized = Boolean.TRUE;
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
myEventDispatcher.getMulticaster().configurationLoaded();
}
});
}
}
});
}
finally {
indicator.popState();
}
}
});
}
private static void convertToBeforeRunTask(Project project, String buildFileUrl, String targetName, String configType, String configName) {
if (buildFileUrl == null || targetName == null || configType == null) {
return;
}
final RunManagerImpl runManager = (RunManagerImpl)RunManagerEx.getInstanceEx(project);
final ConfigurationType type = runManager.getConfigurationType(configType);
if (type == null) {
return;
}
if (configName != null) {
for (RunConfiguration configuration : runManager.getConfigurationsList(type)) {
if (configName.equals(configuration.getName())) {
final List<AntBeforeRunTask> tasks = runManager.getBeforeRunTasks(configuration, AntBeforeRunTaskProvider.ID);
if (!tasks.isEmpty()) {
AntBeforeRunTask task = tasks.get(0);//This is legacy code, we had only one task that time
task.setEnabled(true);
task.setTargetName(targetName);
task.setAntFileUrl(buildFileUrl);
}
}
}
}
else {
for (ConfigurationFactory factory : type.getConfigurationFactories()) {
final RunConfiguration template = runManager.getConfigurationTemplate(factory).getConfiguration();
final List<AntBeforeRunTask> tasks = runManager.getBeforeRunTasks(template, AntBeforeRunTaskProvider.ID);
if (!tasks.isEmpty()) {
AntBeforeRunTask task = tasks.get(0);//This is legacy code, we had only one task that time
task.setEnabled(true);
task.setTargetName(targetName);
task.setAntFileUrl(buildFileUrl);
}
}
}
}
private static void queueLater(final Task task) {
final Application app = ApplicationManager.getApplication();
if (app.isDispatchThread()) {
task.queue();
} else {
app.invokeLater(new Runnable() {
public void run() {
task.queue();
}
});
}
}
public void setContextFile(@NotNull XmlFile file, @Nullable XmlFile context) {
if (context != null) {
myAntFileToContextFileMap.put(file.getVirtualFile(), context.getVirtualFile());
}
else {
myAntFileToContextFileMap.remove(file.getVirtualFile());
}
}
@Nullable
public XmlFile getContextFile(@Nullable final XmlFile file) {
if (file == null) {
return null;
}
final VirtualFile context = myAntFileToContextFileMap.get(file.getVirtualFile());
if (context == null) {
return null;
}
final PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(context);
if (!(psiFile instanceof XmlFile)) {
return null;
}
final XmlFile xmlFile = (XmlFile)psiFile;
return AntDomFileDescription.isAntFile(xmlFile)? xmlFile : null;
}
@Nullable
public AntBuildFileBase getAntBuildFile(@NotNull PsiFile file) {
final VirtualFile vFile = file.getVirtualFile();
if (vFile != null) {
for (AntBuildFileBase bFile : getBuildFiles()) {
if (vFile.equals(bFile.getVirtualFile())) {
return bFile;
}
}
}
return null;
}
@Nullable
public XmlFile getEffectiveContextFile(final XmlFile file) {
return new Object() {
@Nullable XmlFile findContext(final XmlFile file, Set<PsiElement> processed) {
if (file != null) {
processed.add(file);
final XmlFile contextFile = getContextFile(file);
return (contextFile == null || processed.contains(contextFile))? file : findContext(contextFile, processed);
}
return null;
}
}.findContext(file, new HashSet<PsiElement>());
}
private static class EventElementComparator implements Comparator<Element> {
static final Comparator<? super Element> INSTANCE = new EventElementComparator();
private static final String[] COMPARABLE_ATTRIB_NAMES = new String[] {
EVENT_ELEMENT,
TARGET_ELEMENT,
ExecuteCompositeTargetEvent.PRESENTABLE_NAME
};
public int compare(final Element o1, final Element o2) {
for (String attribName : COMPARABLE_ATTRIB_NAMES) {
final int valuesEqual = Comparing.compare(o1.getAttributeValue(attribName), o2.getAttributeValue(attribName));
if (valuesEqual != 0) {
return valuesEqual;
}
}
return 0;
}
}
}