blob: 86f73e9721fa391bd8ea1ddafd01578a20a79283 [file] [log] [blame]
package org.jetbrains.idea.maven.project;
import com.intellij.compiler.CompilerConfiguration;
import com.intellij.compiler.CompilerConfigurationImpl;
import com.intellij.compiler.server.BuildManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.impl.elements.ManifestFileUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.XmlSerializer;
import org.jdom.Document;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.maven.dom.references.MavenFilteredPropertyPsiReferenceProvider;
import org.jetbrains.idea.maven.model.MavenId;
import org.jetbrains.idea.maven.model.MavenResource;
import org.jetbrains.idea.maven.utils.ManifestBuilder;
import org.jetbrains.idea.maven.utils.MavenJDOMUtil;
import org.jetbrains.idea.maven.utils.MavenUtil;
import org.jetbrains.jps.maven.model.impl.*;
import java.io.*;
import java.util.*;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Sergey Evdokimov
*/
public class MavenResourceCompilerConfigurationGenerator {
private static Logger LOG = Logger.getInstance(MavenResourceCompilerConfigurationGenerator.class);
private static final Pattern SIMPLE_NEGATIVE_PATTERN = Pattern.compile("!\\?(\\*\\.\\w+)");
private final Project myProject;
private final MavenProjectsManager myMavenProjectsManager;
private final MavenProjectsTree myProjectsTree;
public MavenResourceCompilerConfigurationGenerator(Project project, MavenProjectsTree projectsTree) {
myProject = project;
myMavenProjectsManager = MavenProjectsManager.getInstance(project);
myProjectsTree = projectsTree;
}
public void generateBuildConfiguration(boolean force) {
if (!myMavenProjectsManager.isMavenizedProject()) {
return;
}
final BuildManager buildManager = BuildManager.getInstance();
final File projectSystemDir = buildManager.getProjectSystemDirectory(myProject);
if (projectSystemDir == null) {
return;
}
final File mavenConfigFile = new File(projectSystemDir, MavenProjectConfiguration.CONFIGURATION_FILE_RELATIVE_PATH);
ProjectRootManager projectRootManager = ProjectRootManager.getInstance(myProject);
ProjectFileIndex fileIndex = projectRootManager.getFileIndex();
final int crc = myProjectsTree.getFilterConfigCrc(fileIndex) + (int)projectRootManager.getModificationCount();
final File crcFile = new File(mavenConfigFile.getParent(), "configuration.crc");
if (!force) {
try {
DataInputStream crcInput = new DataInputStream(new FileInputStream(crcFile));
try {
if (crcInput.readInt() == crc) return; // Project had not change since last config generation.
}
finally {
crcInput.close();
}
}
catch (IOException ignored) {
// // Config file is not generated.
}
}
MavenProjectConfiguration projectConfig = new MavenProjectConfiguration();
for (MavenProject mavenProject : myMavenProjectsManager.getProjects()) {
VirtualFile pomXml = mavenProject.getFile();
Module module = fileIndex.getModuleForFile(pomXml);
if (module == null) continue;
if (!Comparing.equal(mavenProject.getDirectoryFile(), fileIndex.getContentRootForFile(pomXml))) continue;
MavenModuleResourceConfiguration resourceConfig = new MavenModuleResourceConfiguration();
MavenId projectId = mavenProject.getMavenId();
resourceConfig.id = new MavenIdBean(projectId.getGroupId(), projectId.getArtifactId(), projectId.getVersion());
MavenId parentId = mavenProject.getParentId();
if (parentId != null) {
resourceConfig.parentId = new MavenIdBean(parentId.getGroupId(), parentId.getArtifactId(), parentId.getVersion());
}
resourceConfig.directory = FileUtil.toSystemIndependentName(mavenProject.getDirectory());
resourceConfig.delimitersPattern = MavenFilteredPropertyPsiReferenceProvider.getDelimitersPattern(mavenProject).pattern();
for (Map.Entry<String, String> entry : mavenProject.getModelMap().entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (value != null) {
resourceConfig.modelMap.put(key, value);
}
}
Element pluginConfiguration = mavenProject.getPluginConfiguration("org.apache.maven.plugins", "maven-resources-plugin");
resourceConfig.outputDirectory = MavenJDOMUtil.findChildValueByPath(pluginConfiguration, "outputDirectory", null);
addResources(resourceConfig.resources, mavenProject.getResources());
addResources(resourceConfig.testResources, mavenProject.getTestResources());
addWebResources(module, projectConfig, mavenProject);
addEjbClientArtifactConfiguration(module, projectConfig, mavenProject);
resourceConfig.filteringExclusions.addAll(MavenProjectsTree.getFilterExclusions(mavenProject));
final Properties properties = getFilteringProperties(mavenProject);
for (Map.Entry<Object, Object> propEntry : properties.entrySet()) {
resourceConfig.properties.put((String)propEntry.getKey(), (String)propEntry.getValue());
}
resourceConfig.escapeString = MavenJDOMUtil.findChildValueByPath(pluginConfiguration, "escapeString", null);
String escapeWindowsPaths = MavenJDOMUtil.findChildValueByPath(pluginConfiguration, "escapeWindowsPaths");
if (escapeWindowsPaths != null) {
resourceConfig.escapeWindowsPaths = Boolean.parseBoolean(escapeWindowsPaths);
}
String overwrite = MavenJDOMUtil.findChildValueByPath(pluginConfiguration, "overwrite");
if (overwrite != null) {
resourceConfig.overwrite = Boolean.parseBoolean(overwrite);
}
projectConfig.moduleConfigurations.put(module.getName(), resourceConfig);
generateManifest(mavenProject, module);
}
addNonMavenResources(projectConfig);
final Document document = new Document(new Element("maven-project-configuration"));
XmlSerializer.serializeInto(projectConfig, document.getRootElement());
buildManager.runCommand(new Runnable() {
@Override
public void run() {
buildManager.clearState(myProject);
FileUtil.createIfDoesntExist(mavenConfigFile);
try {
JDOMUtil.writeDocument(document, mavenConfigFile, "\n");
DataOutputStream crcOutput = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(crcFile)));
try {
crcOutput.writeInt(crc);
}
finally {
crcOutput.close();
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
private static void generateManifest(@NotNull MavenProject mavenProject, @NotNull Module module) {
try {
String jdkVersion = null;
Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
if (sdk != null && (jdkVersion = sdk.getVersionString()) != null) {
final int quoteIndex = jdkVersion.indexOf('"');
if (quoteIndex != -1) {
jdkVersion = jdkVersion.substring(quoteIndex + 1, jdkVersion.length() - 1);
}
}
Manifest manifest = new ManifestBuilder(mavenProject).withJdkVersion(jdkVersion).build();
File manifestFile = new File(mavenProject.getBuildDirectory(), ManifestFileUtil.MANIFEST_FILE_NAME);
FileUtil.createIfDoesntExist(manifestFile);
OutputStream outputStream = new FileOutputStream(manifestFile);
try {
manifest.write(outputStream);
}
finally {
StreamUtil.closeStream(outputStream);
}
}
catch (ManifestBuilder.ManifestBuilderException e) {
LOG.error("Unable to generate artifact manifest", e);
}
catch (IOException e) {
LOG.error("Unable to save generated artifact manifest", e);
}
}
private Properties getFilteringProperties(MavenProject mavenProject) {
final Properties properties = new Properties();
for (String each : mavenProject.getFilterPropertiesFiles()) {
try {
FileInputStream in = new FileInputStream(each);
try {
properties.load(in);
}
finally {
in.close();
}
}
catch (IOException ignored) {
}
}
properties.putAll(mavenProject.getProperties());
properties.put("settings.localRepository", mavenProject.getLocalRepository().getAbsolutePath());
String jreDir = MavenUtil.getModuleJreHome(myMavenProjectsManager, mavenProject);
if (jreDir != null) {
properties.put("java.home", jreDir);
}
String javaVersion = MavenUtil.getModuleJavaVersion(myMavenProjectsManager, mavenProject);
if (javaVersion != null) {
properties.put("java.version", javaVersion);
}
return properties;
}
private static void addResources(final List<ResourceRootConfiguration> container, @NotNull Collection<MavenResource> resources) {
for (MavenResource resource : resources) {
final String dir = resource.getDirectory();
if (dir == null) {
continue;
}
final ResourceRootConfiguration props = new ResourceRootConfiguration();
props.directory = FileUtil.toSystemIndependentName(dir);
final String target = resource.getTargetPath();
props.targetPath = target != null ? FileUtil.toSystemIndependentName(target) : null;
props.isFiltered = resource.isFiltered();
props.includes.clear();
for (String include : resource.getIncludes()) {
props.includes.add(include.trim());
}
props.excludes.clear();
for (String exclude : resource.getExcludes()) {
props.excludes.add(exclude.trim());
}
container.add(props);
}
}
private static void addWebResources(@NotNull Module module, MavenProjectConfiguration projectCfg, MavenProject mavenProject) {
Element warCfg = mavenProject.getPluginConfiguration("org.apache.maven.plugins", "maven-war-plugin");
if (warCfg == null) return;
boolean filterWebXml = Boolean.parseBoolean(warCfg.getChildTextTrim("filteringDeploymentDescriptors"));
Element webResources = warCfg.getChild("webResources");
if (webResources == null && !filterWebXml) return;
String webArtifactName = MavenUtil.getArtifactName("war", module, true);
MavenWebArtifactConfiguration artifactResourceCfg = projectCfg.webArtifactConfigs.get(webArtifactName);
if (artifactResourceCfg == null) {
artifactResourceCfg = new MavenWebArtifactConfiguration();
artifactResourceCfg.moduleName = module.getName();
projectCfg.webArtifactConfigs.put(webArtifactName, artifactResourceCfg);
}
else {
LOG.error("MavenWebArtifactConfiguration already exists.");
}
if (webResources != null) {
for (Element resource : webResources.getChildren("resource")) {
ResourceRootConfiguration r = new ResourceRootConfiguration();
String directory = resource.getChildTextTrim("directory");
if (StringUtil.isEmptyOrSpaces(directory)) continue;
if (!FileUtil.isAbsolute(directory)) {
directory = mavenProject.getDirectory() + '/' + directory;
}
r.directory = directory;
r.isFiltered = Boolean.parseBoolean(resource.getChildTextTrim("filtering"));
r.targetPath = resource.getChildTextTrim("targetPath");
Element includes = resource.getChild("includes");
if (includes != null) {
for (Element include : includes.getChildren("include")) {
String includeText = include.getTextTrim();
if (!includeText.isEmpty()) {
r.includes.add(includeText);
}
}
}
Element excludes = resource.getChild("excludes");
if (excludes != null) {
for (Element exclude : excludes.getChildren("exclude")) {
String excludeText = exclude.getTextTrim();
if (!excludeText.isEmpty()) {
r.excludes.add(excludeText);
}
}
}
artifactResourceCfg.webResources.add(r);
}
}
if (filterWebXml) {
ResourceRootConfiguration r = new ResourceRootConfiguration();
r.directory = mavenProject.getDirectory() + "/src/main/webapp";
r.includes = Collections.singleton("WEB-INF/web.xml");
r.isFiltered = true;
r.targetPath = "";
artifactResourceCfg.webResources.add(r);
}
}
private void addEjbClientArtifactConfiguration(Module module, MavenProjectConfiguration projectCfg, MavenProject mavenProject) {
Element pluginCfg = mavenProject.getPluginConfiguration("org.apache.maven.plugins", "maven-ejb-plugin");
if (pluginCfg == null || !Boolean.parseBoolean(pluginCfg.getChildTextTrim("generateClient"))) {
return;
}
String ejbClientArtifactName = MavenUtil.getEjbClientArtifactName(module);
MavenEjbClientConfiguration ejbClientCfg = new MavenEjbClientConfiguration();
Element includes = pluginCfg.getChild("clientIncludes");
if (includes != null) {
for (Element include : includes.getChildren("clientInclude")) {
String includeText = include.getTextTrim();
if (!includeText.isEmpty()) {
ejbClientCfg.includes.add(includeText);
}
}
}
Element excludes = pluginCfg.getChild("clientExcludes");
if (excludes != null) {
for (Element exclude : excludes.getChildren("clientExclude")) {
String excludeText = exclude.getTextTrim();
if (!excludeText.isEmpty()) {
ejbClientCfg.excludes.add(excludeText);
}
}
}
if (!ejbClientCfg.isEmpty()) {
projectCfg.ejbClientArtifactConfigs.put(ejbClientArtifactName, ejbClientCfg);
}
}
private void addNonMavenResources(MavenProjectConfiguration projectCfg) {
Set<VirtualFile> processedRoots = new HashSet<VirtualFile>();
for (MavenProject project : myMavenProjectsManager.getProjects()) {
for (String dir : ContainerUtil.concat(project.getSources(), project.getTestSources())) {
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(dir);
if (file != null) {
processedRoots.add(file);
}
}
for (MavenResource resource : ContainerUtil.concat(project.getResources(), project.getTestResources())) {
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(resource.getDirectory());
if (file != null) {
processedRoots.add(file);
}
}
}
CompilerConfiguration compilerConfiguration = CompilerConfiguration.getInstance(myProject);
for (Module module : ModuleManager.getInstance(myProject).getModules()) {
if (!myMavenProjectsManager.isMavenizedModule(module)) continue;
for (ContentEntry contentEntry : ModuleRootManager.getInstance(module).getContentEntries()) {
for (SourceFolder folder : contentEntry.getSourceFolders()) {
VirtualFile file = folder.getFile();
if (file == null) continue;
if (!compilerConfiguration.isExcludedFromCompilation(file) && !isUnderRoots(processedRoots, file)) {
MavenModuleResourceConfiguration configuration = projectCfg.moduleConfigurations.get(module.getName());
if (configuration == null) continue;
List<ResourceRootConfiguration> resourcesList = folder.isTestSource() ? configuration.testResources : configuration.resources;
final ResourceRootConfiguration cfg = new ResourceRootConfiguration();
cfg.directory = FileUtil.toSystemIndependentName(FileUtil.toSystemIndependentName(file.getPath()));
CompilerModuleExtension compilerModuleExtension = CompilerModuleExtension.getInstance(module);
if (compilerModuleExtension == null) continue;
String compilerOutputUrl = folder.isTestSource()
? compilerModuleExtension.getCompilerOutputUrlForTests()
: compilerModuleExtension.getCompilerOutputUrl();
cfg.targetPath = VfsUtil.urlToPath(compilerOutputUrl);
convertIdeaExcludesToMavenExcludes(cfg, (CompilerConfigurationImpl)compilerConfiguration);
resourcesList.add(cfg);
}
}
}
}
}
private static void convertIdeaExcludesToMavenExcludes(ResourceRootConfiguration cfg, CompilerConfigurationImpl compilerConfiguration) {
for (String pattern : compilerConfiguration.getResourceFilePatterns()) {
Matcher matcher = SIMPLE_NEGATIVE_PATTERN.matcher(pattern);
if (matcher.matches()) {
cfg.excludes.add("**/" + matcher.group(1));
}
}
}
private static boolean isUnderRoots(Set<VirtualFile> roots, VirtualFile file) {
for (VirtualFile f = file; f != null; f = f.getParent()) {
if (roots.contains(file)) {
return true;
}
}
return false;
}
}