/*
* 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 org.napile.idea.thermit.config.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.SwingUtilities;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.napile.idea.thermit.ThermitBundle;
import org.napile.idea.thermit.ThermitSupport;
import org.napile.idea.thermit.config.*;
import org.napile.idea.thermit.config.actions.TargetAction;
import org.napile.idea.thermit.dom.AntDomFileDescription;
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.openapi.actionSystem.AnAction;
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.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.components.StorageScheme;
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.Comparing;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.ModificationTracker;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.WriteExternalException;
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;
@State(
name = "ThermitConfiguration",
storages = {
@Storage(file = StoragePathMacros.PROJECT_FILE),
@Storage(file = StoragePathMacros.PROJECT_CONFIG_DIR + "/thermit.xml", scheme = StorageScheme.DIRECTORY_BASED)
}
)
public class ThermitConfigurationImpl extends ThermitConfigurationBase implements PersistentStateComponent<Element>, ModificationTracker
{
public static final ValueProperty<AntReference> DEFAULT_ANT = new ValueProperty<AntReference>("defaultAnt", AntReference.BUNDLED_ANT);
public static final ValueProperty<ThermitConfiguration> INSTANCE = new ValueProperty<ThermitConfiguration>("$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;
ThermitConfiguration thermitConfiguration = ThermitConfigurationImpl.INSTANCE.get(container);
return ProjectRootManager.getInstance(thermitConfiguration.getProject()).getProjectSdkName();
}
public String copy(final String jdkName)
{
return jdkName;
}
};
private static final Logger LOG = Logger.getInstance("#org.napile.idea.thermit.config.impl.ThermitConfigurationImpl");
@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 ThermitWorkspaceConfiguration myAntWorkspaceConfiguration;
private final StartupManager myStartupManager;
private boolean myInitializing;
private volatile long myModificationCount = 0;
public ThermitConfigurationImpl(final Project project, final ThermitWorkspaceConfiguration 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 = ThermitBundle.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(GlobalThermitConfiguration.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);
}
ThermitSupport.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(ThermitConfiguration.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)
{
ThermitSupport.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 = PlatformDataKeys.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 = ThermitBundle.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());
}
}
}
}
ThermitWorkspaceConfiguration.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.getConfigurations(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;
}
}
}