From 6cbdb7ef764142ab86b2628c2e2f510d4e689b22 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Mon, 23 Nov 2015 15:20:02 +0300 Subject: [PATCH] gradle: ear artifacts support --- .../model/project/ModuleDependencyData.java | 23 +- .../JavaEEGradleProjectResolverExtension.java | 100 +++++-- .../plugins/gradle/model/data/Ear.java | 99 +++++++ .../model/data/EarConfigurationModelData.java | 94 +++++++ .../gradle/model/data/EarResource.java | 91 +++++++ .../plugins/gradle/model/data/Jar.java | 74 ++++++ .../plugins/gradle/model/data/War.java | 44 +-- .../BaseGradleProjectResolverExtension.java | 218 ++------------- .../project/GradleProjectResolver.java | 15 +- .../project/GradleProjectResolverUtil.java | 250 +++++++++++++++++- .../gradle/GradleProjectWizardTest.java | 6 +- .../model/ExternalProjectDependency.java | 3 + .../gradle/model/ear/EarConfiguration.java | 66 +++++ .../gradle-tooling-extension-impl.iml | 2 + .../lib/gradle-ear-2.5.jar | Bin 0 -> 63057 bytes ...plugins.gradle.tooling.ModelBuilderService | 1 + .../DefaultExternalProjectDependency.java | 31 ++- .../tooling/builder/CopySpecWalker.groovy | 104 ++++++++ .../builder/EarModelBuilderImpl.groovy | 148 +++++++++++ .../builder/WarModelBuilderImpl.groovy | 75 ++---- .../internal/ear/EarConfigurationImpl.java | 62 +++++ .../tooling/internal/ear/EarModelImpl.java | 85 ++++++ .../tooling/internal/ear/EarResourceImpl.java | 88 ++++++ .../util/DependencyResolverImpl.groovy | 4 +- 24 files changed, 1343 insertions(+), 340 deletions(-) create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/Ear.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/EarConfigurationModelData.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/EarResource.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/Jar.java create mode 100644 plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ear/EarConfiguration.java create mode 100644 plugins/gradle/tooling-extension-impl/lib/gradle-ear-2.5.jar create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/CopySpecWalker.groovy create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/EarModelBuilderImpl.groovy create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarConfigurationImpl.java create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarModelImpl.java create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarResourceImpl.java diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleDependencyData.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleDependencyData.java index b97378d112e7..77fb70041996 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleDependencyData.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleDependencyData.java @@ -2,6 +2,8 @@ package com.intellij.openapi.externalSystem.model.project; import org.jetbrains.annotations.NotNull; +import java.util.Collection; + /** * @author Denis Zhdanov * @since 8/10/11 6:40 PM @@ -10,6 +12,7 @@ public class ModuleDependencyData extends AbstractDependencyData { private static final long serialVersionUID = 1L; private boolean myProductionOnTestDependency; + private Collection myModuleDependencyArtifacts; public ModuleDependencyData(@NotNull ModuleData ownerModule, @NotNull ModuleData module) { super(ownerModule, module); @@ -23,15 +26,31 @@ public class ModuleDependencyData extends AbstractDependencyData { myProductionOnTestDependency = productionOnTestDependency; } + public Collection getModuleDependencyArtifacts() { + return myModuleDependencyArtifacts; + } + + public void setModuleDependencyArtifacts(Collection moduleDependencyArtifacts) { + myModuleDependencyArtifacts = moduleDependencyArtifacts; + } + @Override public boolean equals(Object o) { if (!super.equals(o)) return false; ModuleDependencyData that = (ModuleDependencyData)o; - return myProductionOnTestDependency == that.myProductionOnTestDependency; + if (myProductionOnTestDependency != that.myProductionOnTestDependency) return false; + if (myModuleDependencyArtifacts != null ? !myModuleDependencyArtifacts.equals(that.myModuleDependencyArtifacts) + : that.myModuleDependencyArtifacts != null) { + return false; + } + return true; } @Override public int hashCode() { - return 31 * super.hashCode() + (myProductionOnTestDependency ? 1 : 0); + int result = super.hashCode(); + result = 31 * result + (myProductionOnTestDependency ? 1 : 0); + result = 31 * result + (myModuleDependencyArtifacts != null ? myModuleDependencyArtifacts.hashCode() : 0); + return result; } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/javaee/JavaEEGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/javaee/JavaEEGradleProjectResolverExtension.java index 8b481884763e..753093996591 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/javaee/JavaEEGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/javaee/JavaEEGradleProjectResolverExtension.java @@ -17,21 +17,25 @@ package org.jetbrains.plugins.gradle.integrations.javaee; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.project.DependencyData; import com.intellij.openapi.externalSystem.model.project.ModuleData; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; import com.intellij.openapi.externalSystem.util.Order; +import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.util.BooleanFunction; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.gradle.tooling.model.idea.IdeaModule; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.model.data.*; +import org.jetbrains.plugins.gradle.model.ear.EarConfiguration; import org.jetbrains.plugins.gradle.model.web.WebConfiguration; import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension; +import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil; import org.jetbrains.plugins.gradle.util.GradleConstants; -import java.util.Collections; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -47,33 +51,63 @@ public class JavaEEGradleProjectResolverExtension extends AbstractProjectResolve @Override public void populateModuleExtraModels(@NotNull IdeaModule gradleModule, @NotNull final DataNode ideModule) { - final List warModels; final WebConfiguration webConfiguration = resolverCtx.getExtraProject(gradleModule, WebConfiguration.class); - if (webConfiguration != null) { - warModels = ContainerUtil.map(webConfiguration.getWarModels(), new Function() { - @Override - public War fun(WebConfiguration.WarModel model) { - War war = new War(model.getWarName(), model.getWebAppDirName(), model.getWebAppDir()); - war.setWebXml(model.getWebXml()); - war.setWebResources(map(model.getWebResources())); - war.setClasspath(model.getClasspath()); - war.setManifestContent(model.getManifestContent()); - return war; - } - }); - final String mainSourceSetModuleId = ideModule.getData().getId() + ":main"; - DataNode targetModuleNode = - ExternalSystemApiUtil.find(ideModule, GradleSourceSetData.KEY, new BooleanFunction>() { - @Override - public boolean fun(DataNode node) { - return mainSourceSetModuleId.equals(node.getData().getId()); - } - }); - if(targetModuleNode == null) { - targetModuleNode = ideModule; + final NotNullLazyValue> findTargetModuleNode = new NotNullLazyValue>() { + @NotNull + @Override + protected DataNode compute() { + final String mainSourceSetModuleId = ideModule.getData().getId() + ":main"; + DataNode targetModuleNode = + ExternalSystemApiUtil.find(ideModule, GradleSourceSetData.KEY, new BooleanFunction>() { + @Override + public boolean fun(DataNode node) { + return mainSourceSetModuleId.equals(node.getData().getId()); + } + }); + if (targetModuleNode == null) { + targetModuleNode = ideModule; + } + return targetModuleNode; } - targetModuleNode.createChild(WebConfigurationModelData.KEY, new WebConfigurationModelData(GradleConstants.SYSTEM_ID, warModels)); + }; + if (webConfiguration != null) { + final List warModels = ContainerUtil.map(webConfiguration.getWarModels(), new Function() { + @Override + public War fun(WebConfiguration.WarModel model) { + War war = new War(model.getWarName(), model.getWebAppDirName(), model.getWebAppDir()); + war.setWebXml(model.getWebXml()); + war.setWebResources(mapWebResources(model.getWebResources())); + war.setClasspath(model.getClasspath()); + war.setManifestContent(model.getManifestContent()); + return war; + } + }); + findTargetModuleNode.getValue().createChild( + WebConfigurationModelData.KEY, new WebConfigurationModelData(GradleConstants.SYSTEM_ID, warModels)); + } + + final EarConfiguration earConfiguration = resolverCtx.getExtraProject(gradleModule, EarConfiguration.class); + if (earConfiguration != null) { + final List warModels = ContainerUtil.map(earConfiguration.getEarModels(), new Function() { + @Override + public Ear fun(EarConfiguration.EarModel model) { + Ear ear = new Ear(model.getEarName(), model.getAppDirName(), model.getLibDirName()); + ear.setManifestContent(model.getManifestContent()); + ear.setDeploymentDescriptor(model.getDeploymentDescriptor()); + ear.setResources(mapEarResources(model.getResources())); + return ear; + } + }); + + final Collection deployDependencies = + GradleProjectResolverUtil.getIdeDependencies(findTargetModuleNode.getValue(), earConfiguration.getDeployDependencies()); + final Collection earlibDependencies = + GradleProjectResolverUtil.getIdeDependencies(findTargetModuleNode.getValue(), earConfiguration.getEarlibDependencies()); + + findTargetModuleNode.getValue().createChild( + EarConfigurationModelData.KEY, + new EarConfigurationModelData(GradleConstants.SYSTEM_ID, warModels, deployDependencies, earlibDependencies)); } nextResolver.populateModuleExtraModels(gradleModule, ideModule); } @@ -81,10 +115,10 @@ public class JavaEEGradleProjectResolverExtension extends AbstractProjectResolve @NotNull @Override public Set getExtraProjectModelClasses() { - return Collections.singleton(WebConfiguration.class); + return ContainerUtil.set(WebConfiguration.class, EarConfiguration.class); } - private static List map(List webResources) { + private static List mapWebResources(List webResources) { return ContainerUtil.mapNotNull(webResources, new Function() { @Override public WebResource fun(WebConfiguration.WebResource resource) { @@ -95,4 +129,16 @@ public class JavaEEGradleProjectResolverExtension extends AbstractProjectResolve } }); } + + private static List mapEarResources(List resources) { + return ContainerUtil.mapNotNull(resources, new Function() { + @Override + public EarResource fun(EarConfiguration.EarResource resource) { + if (resource == null) return null; + + return new EarResource(resource.getEarDirectory(), resource.getRelativePath(), resource.getFile()); + } + }); + } + } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/Ear.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/Ear.java new file mode 100644 index 000000000000..39b4240ee3b6 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/Ear.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000-2015 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.jetbrains.plugins.gradle.model.data; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 11/11/2015 + */ +public class Ear extends Jar { + private static final long serialVersionUID = 1L; + + @NotNull + private final String myAppDirName; + @NotNull + private final String myLibDirName; + private String myDeploymentDescriptor; + @NotNull + private List myResources = Collections.emptyList(); + + public Ear(@NotNull String name, @NotNull String appDirName, @NotNull String libDirName) { + super(name); + myAppDirName = appDirName; + myLibDirName = libDirName; + } + + @NotNull + public String getAppDirName() { + return myAppDirName; + } + + @NotNull + public String getLibDirName() { + return myLibDirName; + } + + public void setDeploymentDescriptor(String deploymentDescriptor) { + myDeploymentDescriptor = deploymentDescriptor; + } + + public String getDeploymentDescriptor() { + return myDeploymentDescriptor; + } + + @NotNull + public List getResources() { + return myResources; + } + + public void setResources(@NotNull List resources) { + myResources = resources; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Ear)) return false; + if (!super.equals(o)) return false; + + Ear that = (Ear)o; + if (!myAppDirName.equals(that.myAppDirName)) return false; + if (!myLibDirName.equals(that.myLibDirName)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + myAppDirName.hashCode(); + result = 31 * result + myLibDirName.hashCode(); + return result; + } + + @Override + public String toString() { + return "Ear{" + + "appDirName='" + myAppDirName + '\'' + + ", libDirName='" + myLibDirName + '\'' + + '}'; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/EarConfigurationModelData.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/EarConfigurationModelData.java new file mode 100644 index 000000000000..31989c4dde9e --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/EarConfigurationModelData.java @@ -0,0 +1,94 @@ +/* + * Copyright 2000-2015 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.jetbrains.plugins.gradle.model.data; + +import com.intellij.openapi.externalSystem.model.Key; +import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.model.project.AbstractExternalEntityData; +import com.intellij.openapi.externalSystem.model.project.DependencyData; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 11/11/2015 + */ +public class EarConfigurationModelData extends AbstractExternalEntityData { + private static final long serialVersionUID = 1L; + + @NotNull + public static final Key KEY = + Key.create(EarConfigurationModelData.class, WebConfigurationModelData.KEY.getProcessingWeight() + 1); + + @NotNull + private final List myEars; + @NotNull + private final Collection myDeployDependencies; + @NotNull + private final Collection myEarlibDependencies; + + public EarConfigurationModelData(@NotNull ProjectSystemId owner, + @NotNull List ears, + @NotNull Collection deployDependencies, + @NotNull Collection earlibDependencies) { + super(owner); + myEars = ears; + myDeployDependencies = deployDependencies; + myEarlibDependencies = earlibDependencies; + } + + @NotNull + public List getEars() { + return myEars; + } + + @NotNull + public Collection getDeployDependencies() { + return myDeployDependencies; + } + + @NotNull + public Collection getEarlibDependencies() { + return myEarlibDependencies; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EarConfigurationModelData)) return false; + if (!super.equals(o)) return false; + + EarConfigurationModelData data = (EarConfigurationModelData)o; + + if (!myEars.equals(data.myEars)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + myEars.hashCode(); + return result; + } + + @Override + public String toString() { + return "ears='" + myEars + '\''; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/EarResource.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/EarResource.java new file mode 100644 index 000000000000..12982c4cf0d3 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/EarResource.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2015 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.jetbrains.plugins.gradle.model.data; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.Serializable; + +/** + * @author Vladislav.Soroka + * @since 11/11/2015 + */ +public class EarResource implements Serializable { + private static final long serialVersionUID = 1L; + + @NotNull + private final String myEarDirectory; + @NotNull + private final String relativePath; + @NotNull + private final File file; + + public EarResource(@NotNull String earDirectory, @NotNull String relativePath, @NotNull File file) { + myEarDirectory = earDirectory; + this.relativePath = getAdjustedPath(relativePath); + this.file = file; + } + + @NotNull + public String getEarDirectory() { + return myEarDirectory; + } + + @NotNull + public String getRelativePath() { + return relativePath; + } + + @NotNull + public File getFile() { + return file; + } + + private static String getAdjustedPath(final @NotNull String path) { + return path.isEmpty() || path.charAt(0) != '/' ? '/' + path : path; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EarResource)) return false; + + EarResource resource = (EarResource)o; + if (!file.getPath().equals(resource.file.getPath())) return false; + if (myEarDirectory != resource.myEarDirectory) return false; + if (!relativePath.equals(resource.relativePath)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myEarDirectory.hashCode(); + result = 31 * result + relativePath.hashCode(); + result = 31 * result + file.getPath().hashCode(); + return result; + } + + @Override + public String toString() { + return "Resource{" + + "earDirectory=" + myEarDirectory + + ", relativePath='" + relativePath + '\'' + + ", file=" + file + + '}'; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/Jar.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/Jar.java new file mode 100644 index 000000000000..5f8862fac922 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/Jar.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2015 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.jetbrains.plugins.gradle.model.data; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.Serializable; + +/** + * @author Vladislav.Soroka + * @since 11/21/2015 + */ +public class Jar implements Serializable { + private static final long serialVersionUID = 1L; + + @NotNull + private final String myName; + @Nullable + private String myManifestContent; + + public Jar(@NotNull String name) { + myName = name; + } + + @NotNull + public String getName() { + return myName; + } + + @Nullable + public String getManifestContent() { + return myManifestContent; + } + + public void setManifestContent(@Nullable String manifestContent) { + myManifestContent = manifestContent; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + Jar that = (Jar)o; + if (!myName.equals(that.myName)) return false; + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + myName.hashCode(); + return result; + } + + @Override + public String toString() { + return "Jar{'" + myName + "'}"; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/War.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/War.java index b183324f735c..4a6e8ecc744c 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/War.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/War.java @@ -28,12 +28,9 @@ import java.util.Set; * @author Vladislav.Soroka * @since 2/10/14 */ -public class War implements Serializable { +public class War extends Jar { private static final long serialVersionUID = 1L; - @NotNull - private final String myName; - @NotNull private final String myWebAppDirName; @NotNull @@ -44,23 +41,16 @@ public class War implements Serializable { private List myWebResources; @NotNull private Set myClasspath; - @Nullable - private String myManifestContent; public War(@NotNull String name, @NotNull String webAppDirName, @NotNull File webAppDir) { - myName = name; + super(name); myWebAppDirName = webAppDirName; myWebAppDir = webAppDir; myWebResources = Collections.emptyList(); myClasspath = Collections.emptySet(); } - @NotNull - public String getName() { - return myName; - } - @NotNull public String getWebAppDirName() { return myWebAppDirName; @@ -98,26 +88,17 @@ public class War implements Serializable { return myClasspath; } - public void setManifestContent(@Nullable String manifestContent) { - myManifestContent = manifestContent; - } - - @Nullable - public String getManifestContent() { - return myManifestContent; - } - @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof War)) return false; if (!super.equals(o)) return false; - War that = (War)o; + War war = (War)o; - if (!myName.equals(that.myName)) return false; - if (!myWebAppDirName.equals(that.myWebAppDirName)) return false; - if (!myWebResources.equals(that.myWebResources)) return false; + if (!myWebAppDirName.equals(war.myWebAppDirName)) return false; + if (!myWebResources.equals(war.myWebResources)) return false; + if (!myClasspath.equals(war.myClasspath)) return false; return true; } @@ -125,7 +106,6 @@ public class War implements Serializable { @Override public int hashCode() { int result = super.hashCode(); - result = 31 * result + myName.hashCode(); result = 31 * result + myWebAppDirName.hashCode(); result = 31 * result + myWebResources.hashCode(); return result; @@ -134,11 +114,11 @@ public class War implements Serializable { @Override public String toString() { return "War{" + - "myName='" + myName + '\'' + - ", myWebAppDirName='" + myWebAppDirName + '\'' + - ", myWebAppDir=" + myWebAppDir + - ", myWebXml=" + myWebXml + - ", myWebResources=" + myWebResources + + "name='" + getName() + '\'' + + ", webAppDirName='" + myWebAppDirName + '\'' + + ", webAppDir=" + myWebAppDir + + ", webXml=" + myWebXml + + ", webResources=" + myWebResources + '}'; } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java index 17df607d3987..9658e8f37cd2 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java @@ -47,6 +47,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.net.HttpConfigurable; import com.intellij.util.text.CharArrayUtil; +import gnu.trove.TObjectHashingStrategy; import groovy.lang.GroovyObject; import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling; import org.gradle.tooling.ProjectConnection; @@ -137,14 +138,15 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver // org.gradle.tooling.model.idea.IdeaLanguageLevel.getLevel() returns something like JDK_1_6 final String languageLevel = ideaProject.getLanguageLevel().getLevel(); for (LanguageLevel level : LanguageLevel.values()) { - if(level.name().equals(languageLevel)) { + if (level.name().equals(languageLevel)) { resolvedLanguageLevel = level; break; } } - if(resolvedLanguageLevel != null) { + if (resolvedLanguageLevel != null) { javaProjectData.setLanguageLevel(resolvedLanguageLevel); - } else { + } + else { javaProjectData.setLanguageLevel(languageLevel); } return javaProjectData; @@ -218,14 +220,23 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver sourceSetData.setSourceCompatibility(sourceSet.getSourceCompatibility()); sourceSetData.setTargetCompatibility(sourceSet.getTargetCompatibility()); + final Set artifacts = ContainerUtil.newTroveSet(FileUtil.FILE_HASHING_STRATEGY); if ("main".equals(sourceSet.getName())) { - final List artifacts = ContainerUtil.newArrayList(); final Set defaultArtifacts = externalProject.getArtifactsByConfiguration().get("default"); if (defaultArtifacts != null) { artifacts.addAll(defaultArtifacts); } - sourceSetData.setArtifacts(artifacts); + final Set archivesArtifacts = externalProject.getArtifactsByConfiguration().get("archives"); + if (archivesArtifacts != null) { + artifacts.addAll(archivesArtifacts); + } + } else if("test".equals(sourceSet.getName())) { + final Set testsArtifacts = externalProject.getArtifactsByConfiguration().get("tests"); + if (testsArtifacts != null) { + artifacts.addAll(testsArtifacts); + } } + sourceSetData.setArtifacts(ContainerUtil.newArrayList(artifacts)); DataNode sourceSetDataNode = mainModuleNode.createChild(GradleSourceSetData.KEY, sourceSetData); final Map, ExternalSourceSet>> sourceSetMap = @@ -407,60 +418,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver processSourceSets(externalProject, ideModule, new SourceSetsProcessor() { @Override public void process(@NotNull DataNode dataNode, @NotNull ExternalSourceSet sourceSet) { - Map dependencyMap = ContainerUtil.newHashMap(); - - Queue queue = ContainerUtil.newLinkedList(sourceSet.getDependencies()); - while (!queue.isEmpty()) { - final ExternalDependency dependency = queue.remove(); - ExternalDependency seenDependency = dependencyMap.get(dependency.getId()); - if (seenDependency != null) { - if (dependency instanceof ExternalLibraryDependency) { - if (seenDependency instanceof ExternalLibraryDependency && - !FileUtil.filesEqual(((ExternalLibraryDependency)seenDependency).getFile(), - ((ExternalLibraryDependency)dependency).getFile())) { - DefaultExternalMultiLibraryDependency mergedDependency = new DefaultExternalMultiLibraryDependency(); - mergedDependency.setName(dependency.getId().getName()); - mergedDependency.setGroup(dependency.getId().getGroup()); - mergedDependency.setVersion(dependency.getId().getVersion()); - mergedDependency.setPackaging(dependency.getId().getPackaging()); - mergedDependency.setClassifier(dependency.getId().getClassifier()); - mergedDependency.setScope(dependency.getScope()); - mergedDependency.setClasspathOrder(dependency.getClasspathOrder()); - mergedDependency.getDependencies().addAll(dependency.getDependencies()); - - mergedDependency.getFiles().addAll(ContainerUtil.packNullables( - ((ExternalLibraryDependency)seenDependency).getFile(), ((ExternalLibraryDependency)dependency).getFile())); - mergedDependency.getSources().addAll((ContainerUtil.packNullables( - ((ExternalLibraryDependency)seenDependency).getSource(), ((ExternalLibraryDependency)dependency).getSource()))); - mergedDependency.getJavadoc().addAll((ContainerUtil.packNullables( - ((ExternalLibraryDependency)seenDependency).getJavadoc(), ((ExternalLibraryDependency)dependency).getJavadoc()))); - - dependencyMap.put(dependency.getId(), mergedDependency); - continue; - } - else if (seenDependency instanceof DefaultExternalMultiLibraryDependency) { - DefaultExternalMultiLibraryDependency mergedDependency = (DefaultExternalMultiLibraryDependency)seenDependency; - mergedDependency.getFiles().addAll(ContainerUtil.packNullables(((ExternalLibraryDependency)dependency).getFile())); - mergedDependency.getSources().addAll(ContainerUtil.packNullables(((ExternalLibraryDependency)dependency).getSource())); - mergedDependency.getJavadoc().addAll(ContainerUtil.packNullables(((ExternalLibraryDependency)dependency).getJavadoc())); - continue; - } - } - - DependencyScope prevScope = - seenDependency.getScope() == null ? DependencyScope.COMPILE : DependencyScope.valueOf(seenDependency.getScope()); - DependencyScope currentScope = - dependency.getScope() == null ? DependencyScope.COMPILE : DependencyScope.valueOf(dependency.getScope()); - - if (prevScope.isForProductionCompile()) continue; - if (prevScope.isForProductionRuntime() && currentScope.isForProductionRuntime()) continue; - } - - dependencyMap.put(new DefaultExternalDependencyId(dependency.getId()), dependency); - queue.addAll(dependency.getDependencies()); - } - - buildSourceSetDependency(sourceSetMap, dependencyMap, dataNode, sourceSet.getDependencies(), ideProject); + buildDependencies(sourceSetMap, dataNode, sourceSet.getDependencies(), ideProject); } }); @@ -557,10 +515,8 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver @Override public Set getExtraProjectModelClasses() { Set result = ContainerUtil.set(GradleBuild.class, ModuleExtendedModel.class); - if (!ExternalSystemApiUtil.isInProcessMode(GradleConstants.SYSTEM_ID) || !resolverCtx.isPreviewMode()) { - result.add(BuildScriptClasspathModel.class); - } - result.add(resolverCtx.isPreviewMode() ? ExternalProjectPreview.class : ExternalProject.class); + result.add(BuildScriptClasspathModel.class); + result.add(ExternalProject.class); return result; } @@ -869,7 +825,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver @NotNull DataNode ownerModule, @NotNull IdeaSingleEntryLibraryDependency dependency, @NotNull DataNode ideProject) - throws IllegalStateException { + throws IllegalStateException { File binaryPath = dependency.getFile(); if (binaryPath == null) { throw new IllegalStateException(String.format( @@ -961,145 +917,13 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver library.addPath(LibraryPathType.DOC, javadocPath.getAbsolutePath()); } - if(level == LibraryLevel.PROJECT) { + if (level == LibraryLevel.PROJECT) { linkProjectLibrary(ideProject, library); } return new LibraryDependencyData(ownerModule.getData(), library, level); } - private static void buildSourceSetDependency(@NotNull Map, ExternalSourceSet>> sourceSetMap, - @NotNull Map mergedDependencyMap, - @NotNull DataNode ownerDataNode, - @NotNull Collection dependencies, - @NotNull DataNode ideProject) throws IllegalStateException { - - Map dependencyMap = ContainerUtil.newLinkedHashMap(); - for (ExternalDependency dependency : dependencies) { - dependencyMap.put(dependency.getId(), dependency); - } - - for (ExternalDependency dependency : dependencyMap.values()) { - final ExternalDependency mergedDependency = ContainerUtil.getOrElse(mergedDependencyMap, dependency.getId(), dependency); - DependencyScope dependencyScope = getDependencyScope(mergedDependency.getScope()); - - ModuleData ownerModule = null; - if (ownerDataNode.getData() instanceof ModuleData) { - ownerModule = (ModuleData)ownerDataNode.getData(); - } - else if (ownerDataNode.getData() instanceof DependencyData) { - ownerModule = ((DependencyData)ownerDataNode.getData()).getOwnerModule(); - } - - assert ownerModule != null; - - if (mergedDependency instanceof ExternalProjectDependency) { - String moduleId = getModuleId((ExternalProjectDependency)mergedDependency); - Pair, ExternalSourceSet> projectPair = sourceSetMap.get(moduleId); - ModuleDependencyData moduleDependencyData = new ModuleDependencyData(ownerModule, projectPair.first.getData()); - moduleDependencyData.setScope(dependencyScope); - if ("test".equals(projectPair.second.getName())) { - moduleDependencyData.setProductionOnTestDependency(true); - } - moduleDependencyData.setOrder(mergedDependency.getClasspathOrder()); - moduleDependencyData.setExported(mergedDependency.getExported()); - DataNode ideModuleDependencyNode = - ownerDataNode.createChild(ProjectKeys.MODULE_DEPENDENCY, moduleDependencyData); - buildSourceSetDependency(sourceSetMap, mergedDependencyMap, ideModuleDependencyNode, dependency.getDependencies(), ideProject); - } - if (mergedDependency instanceof ExternalLibraryDependency) { - final LibraryLevel level = LibraryLevel.PROJECT; - String libraryName = mergedDependency.getId().getPresentableName(); - final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName); - LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule, library, level); - libraryDependencyData.setScope(dependencyScope); - libraryDependencyData.setOrder(mergedDependency.getClasspathOrder()); - libraryDependencyData.setExported(mergedDependency.getExported()); - - library.addPath(LibraryPathType.BINARY, ((ExternalLibraryDependency)mergedDependency).getFile().getAbsolutePath()); - File sourcePath = ((ExternalLibraryDependency)mergedDependency).getSource(); - if (sourcePath != null) { - library.addPath(LibraryPathType.SOURCE, sourcePath.getAbsolutePath()); - } - File javaDocPath = ((ExternalLibraryDependency)mergedDependency).getJavadoc(); - if (javaDocPath != null) { - library.addPath(LibraryPathType.DOC, javaDocPath.getAbsolutePath()); - } - DataNode libraryDependencyDataNode = - ownerDataNode.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData); - - linkProjectLibrary(ideProject, library); - - buildSourceSetDependency(sourceSetMap, mergedDependencyMap, libraryDependencyDataNode, dependency.getDependencies(), ideProject); - } - if (mergedDependency instanceof ExternalMultiLibraryDependency) { - final LibraryLevel level = LibraryLevel.MODULE; - String libraryName = mergedDependency.getId().getPresentableName(); - final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName); - LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule, library, level); - libraryDependencyData.setScope(dependencyScope); - libraryDependencyData.setOrder(mergedDependency.getClasspathOrder()); - libraryDependencyData.setExported(mergedDependency.getExported()); - - for (File file : ((ExternalMultiLibraryDependency)mergedDependency).getFiles()) { - library.addPath(LibraryPathType.BINARY, file.getAbsolutePath()); - } - for (File file : ((ExternalMultiLibraryDependency)mergedDependency).getSources()) { - library.addPath(LibraryPathType.SOURCE, file.getAbsolutePath()); - } - for (File file : ((ExternalMultiLibraryDependency)mergedDependency).getJavadoc()) { - library.addPath(LibraryPathType.DOC, file.getAbsolutePath()); - } - - DataNode libraryDependencyDataNode = - ownerDataNode.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData); - - buildSourceSetDependency(sourceSetMap, mergedDependencyMap, libraryDependencyDataNode, dependency.getDependencies(), ideProject); - } - if (mergedDependency instanceof FileCollectionDependency) { - final LibraryLevel level = LibraryLevel.MODULE; - String libraryName = ""; - final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName); - LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule, library, level); - libraryDependencyData.setScope(dependencyScope); - libraryDependencyData.setOrder(mergedDependency.getClasspathOrder()); - libraryDependencyData.setExported(mergedDependency.getExported()); - - for (File file : ((FileCollectionDependency)mergedDependency).getFiles()) { - library.addPath(LibraryPathType.BINARY, file.getAbsolutePath()); - } - - ownerDataNode.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData); - } - if (mergedDependency instanceof UnresolvedExternalDependency) { - final LibraryLevel level = LibraryLevel.PROJECT; - String libraryName = mergedDependency.getId().getPresentableName(); - final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName, true); - LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule, library, level); - libraryDependencyData.setScope(dependencyScope); - final String failureMessage = ((UnresolvedExternalDependency)mergedDependency).getFailureMessage(); - if (failureMessage != null) { - library.addPath(LibraryPathType.BINARY, failureMessage); - } - ownerDataNode.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData); - linkProjectLibrary(ideProject, library); - } - } - } - - private static void linkProjectLibrary(@NotNull DataNode ideProject, @NotNull final LibraryData library) { - DataNode libraryData = - ExternalSystemApiUtil.find(ideProject, ProjectKeys.LIBRARY, new BooleanFunction>() { - @Override - public boolean fun(DataNode node) { - return library.equals(node.getData()); - } - }); - if (libraryData == null) { - ideProject.createChild(ProjectKeys.LIBRARY, library); - } - } - interface SourceSetsProcessor { void process(@NotNull DataNode dataNode, @NotNull ExternalSourceSet sourceSet); } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java index 7f2ea67a5bb8..5f7cf97109d6 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java @@ -142,6 +142,12 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver commandLineArgs = ContainerUtil.newArrayList(); final Set toolingExtensionClasses = ContainerUtil.newHashSet(); + if(resolverCtx.isPreviewMode()){ + commandLineArgs.add("-Didea.isPreviewMode=true"); + final Set previewLightWeightToolingModels = ContainerUtil.set(ExternalProjectPreview.class); + projectImportAction.addExtraProjectModelClasses(previewLightWeightToolingModels); + } + final GradleImportCustomizer importCustomizer = GradleImportCustomizer.get(); for (GradleProjectResolverExtension resolverExtension = projectResolverChain; resolverExtension != null; @@ -150,8 +156,11 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver moduleDataNode = pair.first; final IdeaModule ideaModule = pair.second; projectResolverChain.populateModuleDependencies(ideaModule, moduleDataNode, projectDataNode); + projectResolverChain.populateModuleExtraModels(ideaModule, moduleDataNode); } mergeSourceSetContentRoots(moduleMap, resolverCtx); mergeLibraryAndModuleDependencyData(projectDataNode, gradleHomeDir, gradleVersion); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverUtil.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverUtil.java index f86e16f96c67..c53def95c073 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverUtil.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverUtil.java @@ -16,34 +16,42 @@ package org.jetbrains.plugins.gradle.service.project; import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.Key; import com.intellij.openapi.externalSystem.model.ProjectKeys; -import com.intellij.openapi.externalSystem.model.project.LibraryData; -import com.intellij.openapi.externalSystem.model.project.LibraryPathType; -import com.intellij.openapi.externalSystem.model.project.ModuleData; -import com.intellij.openapi.externalSystem.model.project.ProjectData; +import com.intellij.openapi.externalSystem.model.project.*; import com.intellij.openapi.externalSystem.model.task.TaskData; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; import com.intellij.openapi.roots.DependencyScope; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.BooleanFunction; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import org.gradle.tooling.model.GradleProject; import org.gradle.tooling.model.idea.IdeaModule; import org.gradle.util.GradleVersion; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.gradle.model.BuildScriptClasspathModel; -import org.jetbrains.plugins.gradle.model.ExternalProject; -import org.jetbrains.plugins.gradle.model.ExternalProjectDependency; -import org.jetbrains.plugins.gradle.model.ExternalSourceSet; +import org.jetbrains.plugins.gradle.DefaultExternalDependencyId; +import org.jetbrains.plugins.gradle.ExternalDependencyId; +import org.jetbrains.plugins.gradle.model.*; +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData; +import org.jetbrains.plugins.gradle.util.GradleConstants; import java.io.File; +import java.util.Collection; +import java.util.Map; +import java.util.Queue; /** * @author Vladislav.Soroka * @since 10/6/2015 */ public class GradleProjectResolverUtil { + @NotNull + private static final Key CONTAINER_KEY = Key.create(Object.class, ExternalSystemConstants.UNORDERED); @NotNull public static String getModuleId(@NotNull IdeaModule gradleModule) { @@ -128,6 +136,232 @@ public class GradleProjectResolverUtil { } } + @SuppressWarnings("unchecked") + public static Collection getIdeDependencies(@NotNull DataNode moduleDataNode, + @NotNull Collection dependencies) + throws IllegalStateException { + + final DataNode ideProject = ExternalSystemApiUtil.findParent(moduleDataNode, ProjectKeys.PROJECT); + assert ideProject != null; + + final Map, ExternalSourceSet>> sourceSetMap = + ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS); + assert sourceSetMap != null; + + DataNode fakeNode = new DataNode(CONTAINER_KEY, moduleDataNode.getData(), null); + buildDependencies(sourceSetMap, fakeNode, dependencies, null); + final Collection> dataNodes = + ExternalSystemApiUtil.findAllRecursively(fakeNode, new BooleanFunction>() { + @Override + public boolean fun(DataNode node) { + return node.getData() instanceof DependencyData; + } + }); + return ContainerUtil.map(dataNodes, new Function, DependencyData>() { + @Override + public DependencyData fun(DataNode node) { + return (DependencyData)node.getData(); + } + }); + } + + public static void buildDependencies(@NotNull Map, ExternalSourceSet>> sourceSetMap, + @NotNull DataNode ownerDataNode, + @NotNull Collection dependencies, + @Nullable DataNode ideProject) throws IllegalStateException { + Map dependencyMap = ContainerUtil.newHashMap(); + + Queue queue = ContainerUtil.newLinkedList(dependencies); + while (!queue.isEmpty()) { + final ExternalDependency dependency = queue.remove(); + ExternalDependency seenDependency = dependencyMap.get(dependency.getId()); + if (seenDependency != null) { + if (dependency instanceof ExternalLibraryDependency) { + if (seenDependency instanceof ExternalLibraryDependency && + !FileUtil.filesEqual(((ExternalLibraryDependency)seenDependency).getFile(), + ((ExternalLibraryDependency)dependency).getFile())) { + DefaultExternalMultiLibraryDependency mergedDependency = new DefaultExternalMultiLibraryDependency(); + mergedDependency.setName(dependency.getId().getName()); + mergedDependency.setGroup(dependency.getId().getGroup()); + mergedDependency.setVersion(dependency.getId().getVersion()); + mergedDependency.setPackaging(dependency.getId().getPackaging()); + mergedDependency.setClassifier(dependency.getId().getClassifier()); + mergedDependency.setScope(dependency.getScope()); + mergedDependency.setClasspathOrder(dependency.getClasspathOrder()); + mergedDependency.getDependencies().addAll(dependency.getDependencies()); + + mergedDependency.getFiles().addAll(ContainerUtil.packNullables( + ((ExternalLibraryDependency)seenDependency).getFile(), ((ExternalLibraryDependency)dependency).getFile())); + mergedDependency.getSources().addAll((ContainerUtil.packNullables( + ((ExternalLibraryDependency)seenDependency).getSource(), ((ExternalLibraryDependency)dependency).getSource()))); + mergedDependency.getJavadoc().addAll((ContainerUtil.packNullables( + ((ExternalLibraryDependency)seenDependency).getJavadoc(), ((ExternalLibraryDependency)dependency).getJavadoc()))); + + dependencyMap.put(dependency.getId(), mergedDependency); + continue; + } + else if (seenDependency instanceof DefaultExternalMultiLibraryDependency) { + DefaultExternalMultiLibraryDependency mergedDependency = (DefaultExternalMultiLibraryDependency)seenDependency; + mergedDependency.getFiles().addAll(ContainerUtil.packNullables(((ExternalLibraryDependency)dependency).getFile())); + mergedDependency.getSources().addAll(ContainerUtil.packNullables(((ExternalLibraryDependency)dependency).getSource())); + mergedDependency.getJavadoc().addAll(ContainerUtil.packNullables(((ExternalLibraryDependency)dependency).getJavadoc())); + continue; + } + } + + DependencyScope prevScope = + seenDependency.getScope() == null ? DependencyScope.COMPILE : DependencyScope.valueOf(seenDependency.getScope()); + DependencyScope currentScope = + dependency.getScope() == null ? DependencyScope.COMPILE : DependencyScope.valueOf(dependency.getScope()); + + if (prevScope.isForProductionCompile()) continue; + if (prevScope.isForProductionRuntime() && currentScope.isForProductionRuntime()) continue; + } + + dependencyMap.put(new DefaultExternalDependencyId(dependency.getId()), dependency); + queue.addAll(dependency.getDependencies()); + } + + doBuildDependencies(sourceSetMap, dependencyMap, ownerDataNode, dependencies, ideProject); + } + + private static void doBuildDependencies(@NotNull Map, ExternalSourceSet>> sourceSetMap, + @NotNull Map mergedDependencyMap, + @NotNull DataNode ownerDataNode, + @NotNull Collection dependencies, + @Nullable DataNode ideProject) throws IllegalStateException { + + Map dependencyMap = ContainerUtil.newLinkedHashMap(); + for (ExternalDependency dependency : dependencies) { + dependencyMap.put(dependency.getId(), dependency); + } + + for (ExternalDependency dependency : dependencyMap.values()) { + final ExternalDependency mergedDependency = ContainerUtil.getOrElse(mergedDependencyMap, dependency.getId(), dependency); + DependencyScope dependencyScope = getDependencyScope(mergedDependency.getScope()); + + ModuleData ownerModule = null; + if (ownerDataNode.getData() instanceof ModuleData) { + ownerModule = (ModuleData)ownerDataNode.getData(); + } + else if (ownerDataNode.getData() instanceof DependencyData) { + ownerModule = ((DependencyData)ownerDataNode.getData()).getOwnerModule(); + } + + assert ownerModule != null; + + if (mergedDependency instanceof ExternalProjectDependency) { + final ExternalProjectDependency projectDependency = (ExternalProjectDependency)mergedDependency; + String moduleId = getModuleId(projectDependency); + Pair, ExternalSourceSet> projectPair = sourceSetMap.get(moduleId); + ModuleDependencyData moduleDependencyData = new ModuleDependencyData(ownerModule, projectPair.first.getData()); + moduleDependencyData.setScope(dependencyScope); + if ("test".equals(projectPair.second.getName())) { + moduleDependencyData.setProductionOnTestDependency(true); + } + moduleDependencyData.setOrder(mergedDependency.getClasspathOrder()); + moduleDependencyData.setExported(mergedDependency.getExported()); + moduleDependencyData.setModuleDependencyArtifacts(projectDependency.getProjectDependencyArtifacts()); + + DataNode ideModuleDependencyNode = + ownerDataNode.createChild(ProjectKeys.MODULE_DEPENDENCY, moduleDependencyData); + doBuildDependencies(sourceSetMap, mergedDependencyMap, ideModuleDependencyNode, dependency.getDependencies(), ideProject); + } + if (mergedDependency instanceof ExternalLibraryDependency) { + final LibraryLevel level = LibraryLevel.PROJECT; + String libraryName = mergedDependency.getId().getPresentableName(); + final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName); + LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule, library, level); + libraryDependencyData.setScope(dependencyScope); + libraryDependencyData.setOrder(mergedDependency.getClasspathOrder()); + libraryDependencyData.setExported(mergedDependency.getExported()); + + library.addPath(LibraryPathType.BINARY, ((ExternalLibraryDependency)mergedDependency).getFile().getAbsolutePath()); + File sourcePath = ((ExternalLibraryDependency)mergedDependency).getSource(); + if (sourcePath != null) { + library.addPath(LibraryPathType.SOURCE, sourcePath.getAbsolutePath()); + } + File javaDocPath = ((ExternalLibraryDependency)mergedDependency).getJavadoc(); + if (javaDocPath != null) { + library.addPath(LibraryPathType.DOC, javaDocPath.getAbsolutePath()); + } + DataNode libraryDependencyDataNode = + ownerDataNode.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData); + + linkProjectLibrary(ideProject, library); + + doBuildDependencies(sourceSetMap, mergedDependencyMap, libraryDependencyDataNode, dependency.getDependencies(), ideProject); + } + if (mergedDependency instanceof ExternalMultiLibraryDependency) { + final LibraryLevel level = LibraryLevel.MODULE; + String libraryName = mergedDependency.getId().getPresentableName(); + final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName); + LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule, library, level); + libraryDependencyData.setScope(dependencyScope); + libraryDependencyData.setOrder(mergedDependency.getClasspathOrder()); + libraryDependencyData.setExported(mergedDependency.getExported()); + + for (File file : ((ExternalMultiLibraryDependency)mergedDependency).getFiles()) { + library.addPath(LibraryPathType.BINARY, file.getAbsolutePath()); + } + for (File file : ((ExternalMultiLibraryDependency)mergedDependency).getSources()) { + library.addPath(LibraryPathType.SOURCE, file.getAbsolutePath()); + } + for (File file : ((ExternalMultiLibraryDependency)mergedDependency).getJavadoc()) { + library.addPath(LibraryPathType.DOC, file.getAbsolutePath()); + } + + DataNode libraryDependencyDataNode = + ownerDataNode.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData); + + doBuildDependencies(sourceSetMap, mergedDependencyMap, libraryDependencyDataNode, dependency.getDependencies(), ideProject); + } + if (mergedDependency instanceof FileCollectionDependency) { + final LibraryLevel level = LibraryLevel.MODULE; + String libraryName = ""; + final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName); + LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule, library, level); + libraryDependencyData.setScope(dependencyScope); + libraryDependencyData.setOrder(mergedDependency.getClasspathOrder()); + libraryDependencyData.setExported(mergedDependency.getExported()); + + for (File file : ((FileCollectionDependency)mergedDependency).getFiles()) { + library.addPath(LibraryPathType.BINARY, file.getAbsolutePath()); + } + + ownerDataNode.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData); + } + if (mergedDependency instanceof UnresolvedExternalDependency) { + final LibraryLevel level = LibraryLevel.PROJECT; + String libraryName = mergedDependency.getId().getPresentableName(); + final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName, true); + LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule, library, level); + libraryDependencyData.setScope(dependencyScope); + final String failureMessage = ((UnresolvedExternalDependency)mergedDependency).getFailureMessage(); + if (failureMessage != null) { + library.addPath(LibraryPathType.BINARY, failureMessage); + } + ownerDataNode.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData); + linkProjectLibrary(ideProject, library); + } + } + } + + public static void linkProjectLibrary(@Nullable DataNode ideProject, @NotNull final LibraryData library) { + if (ideProject == null) return; + + DataNode libraryData = + ExternalSystemApiUtil.find(ideProject, ProjectKeys.LIBRARY, new BooleanFunction>() { + @Override + public boolean fun(DataNode node) { + return library.equals(node.getData()); + } + }); + if (libraryData == null) { + ideProject.createChild(ProjectKeys.LIBRARY, library); + } + } + public static boolean isIdeaTask(final String taskName, @Nullable String group) { if ((group == null || "ide".equalsIgnoreCase(group)) && StringUtil.containsIgnoreCase(taskName, "idea")) return true; return "other".equalsIgnoreCase(group) && StringUtil.containsIgnoreCase(taskName, "idea"); diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/GradleProjectWizardTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/GradleProjectWizardTest.java index 300225e6f810..40fe15abf229 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/GradleProjectWizardTest.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/GradleProjectWizardTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2013 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. @@ -62,12 +62,12 @@ public class GradleProjectWizardTest extends NewProjectWizardTestCase { assertEquals(projectName, module.getName()); VirtualFile root = ProjectRootManager.getInstance(project).getContentRoots()[0]; - VirtualFile settingsScript = root.findChild("settings.gradle"); + VirtualFile settingsScript = VfsUtilCore.findRelativeFile("settings.gradle", root); assertNotNull(settingsScript); assertEquals(String.format("rootProject.name = '%s'\n\n", projectName), StringUtil.convertLineSeparators(VfsUtilCore.loadText(settingsScript))); - VirtualFile buildScript = root.findChild("build.gradle"); + VirtualFile buildScript = VfsUtilCore.findRelativeFile("build.gradle", root); assertNotNull(buildScript); assertEquals("group '" + projectName + "'\n" + "version '1.0-SNAPSHOT'\n" + diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProjectDependency.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProjectDependency.java index 47dfdee26d0f..524e8258a793 100644 --- a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProjectDependency.java +++ b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProjectDependency.java @@ -15,6 +15,8 @@ */ package org.jetbrains.plugins.gradle.model; +import java.util.Collection; + /** * @author Vladislav.Soroka * @since 7/14/2014 @@ -23,4 +25,5 @@ public interface ExternalProjectDependency extends ExternalDependency { String getProjectPath(); + Collection getProjectDependencyArtifacts(); } diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ear/EarConfiguration.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ear/EarConfiguration.java new file mode 100644 index 000000000000..ef518352cd43 --- /dev/null +++ b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ear/EarConfiguration.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2015 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.jetbrains.plugins.gradle.model.ear; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.model.ExternalDependency; + +import java.io.File; +import java.io.Serializable; +import java.util.Collection; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 11/10/2015 + */ +public interface EarConfiguration extends Serializable { + List getEarModels(); + + @NotNull + Collection getDeployDependencies(); + + @NotNull + Collection getEarlibDependencies(); + + interface EarModel extends Serializable { + @NotNull + String getEarName(); + + String getManifestContent(); + + @NotNull + String getAppDirName(); + + String getLibDirName(); + + List getResources(); + + String getDeploymentDescriptor(); + } + + interface EarResource extends Serializable { + + @NotNull + String getEarDirectory(); + + @NotNull + String getRelativePath(); + + @NotNull + File getFile(); + } +} diff --git a/plugins/gradle/tooling-extension-impl/gradle-tooling-extension-impl.iml b/plugins/gradle/tooling-extension-impl/gradle-tooling-extension-impl.iml index c5ce2d33e492..10d19638bf77 100644 --- a/plugins/gradle/tooling-extension-impl/gradle-tooling-extension-impl.iml +++ b/plugins/gradle/tooling-extension-impl/gradle-tooling-extension-impl.iml @@ -68,10 +68,12 @@ + + diff --git a/plugins/gradle/tooling-extension-impl/lib/gradle-ear-2.5.jar b/plugins/gradle/tooling-extension-impl/lib/gradle-ear-2.5.jar new file mode 100644 index 0000000000000000000000000000000000000000..dcab048bb8a5e49ebd01b7ca1a8ea2d96a57bb05 GIT binary patch literal 63057 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 literal 0 HcmV?d00001 diff --git a/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService b/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService index 816ef05c1e4d..d5580bcf4b3c 100644 --- a/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService +++ b/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService @@ -14,6 +14,7 @@ # limitations under the License. # org.jetbrains.plugins.gradle.tooling.builder.WarModelBuilderImpl +org.jetbrains.plugins.gradle.tooling.builder.EarModelBuilderImpl org.jetbrains.plugins.gradle.tooling.builder.ModuleExtendedModelBuilderImpl org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImpl org.jetbrains.plugins.gradle.tooling.builder.ScalaModelBuilderImpl diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalProjectDependency.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalProjectDependency.java index 9bb9c64e9fd0..d97895ffa767 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalProjectDependency.java +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalProjectDependency.java @@ -17,6 +17,9 @@ package org.jetbrains.plugins.gradle.model; import com.google.common.base.Objects; +import java.util.ArrayList; +import java.util.Collection; + /** * @author Vladislav.Soroka * @since 12/24/2014 @@ -25,23 +28,37 @@ public class DefaultExternalProjectDependency extends AbstractExternalDependency private static final long serialVersionUID = 1L; - private String projectPath; + private String myProjectPath; + private Collection myProjectDependencyArtifacts; public DefaultExternalProjectDependency() { } public DefaultExternalProjectDependency(ExternalProjectDependency dependency) { super(dependency); - projectPath = dependency.getProjectPath(); + myProjectPath = dependency.getProjectPath(); + myProjectDependencyArtifacts = + dependency.getProjectDependencyArtifacts() == null + ? new ArrayList() + : new ArrayList(dependency.getProjectDependencyArtifacts()); } @Override public String getProjectPath() { - return projectPath; + return myProjectPath; } public void setProjectPath(String projectPath) { - this.projectPath = projectPath; + this.myProjectPath = projectPath; + } + + @Override + public Collection getProjectDependencyArtifacts() { + return myProjectDependencyArtifacts; + } + + public void setProjectDependencyArtifacts(Collection projectArtifacts) { + myProjectDependencyArtifacts = projectArtifacts; } @Override @@ -50,16 +67,16 @@ public class DefaultExternalProjectDependency extends AbstractExternalDependency if (!(o instanceof DefaultExternalProjectDependency)) return false; if (!super.equals(o)) return false; DefaultExternalProjectDependency that = (DefaultExternalProjectDependency)o; - return Objects.equal(projectPath, that.projectPath); + return Objects.equal(myProjectPath, that.myProjectPath); } @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), projectPath); + return Objects.hashCode(super.hashCode(), myProjectPath); } @Override public String toString() { - return "project dependency '" + projectPath + '\''; + return "project dependency '" + myProjectPath + '\''; } } diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/CopySpecWalker.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/CopySpecWalker.groovy new file mode 100644 index 000000000000..635b13a0df65 --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/CopySpecWalker.groovy @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2015 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.jetbrains.plugins.gradle.tooling.builder + +import org.gradle.api.Action +import org.gradle.api.file.CopySpec +import org.gradle.api.file.FileVisitDetails +import org.gradle.api.file.FileVisitor +import org.gradle.util.GradleVersion + +/** + * @author Vladislav.Soroka + * @since 11/10/2015 + */ +class CopySpecWalker { + + static interface Visitor { + void visitSourcePath(String relativePath, String path) + + void visitDir(String relativePath, FileVisitDetails dirDetails) + + void visitFile(String relativePath, FileVisitDetails fileDetails) + } + + @SuppressWarnings("GrUnresolvedAccess") + static void walk(CopySpec copySpec, Visitor visitor) { + copySpec.setIncludeEmptyDirs(true) + if (!(copySpec.metaClass.respondsTo(copySpec, 'walk', Action))) { + return + } + + copySpec.walk({ def resolver -> + // def resolver -> + // in Gradle v1.x - org.gradle.api.internal.file.copy.CopySpecInternal + // in Gradle v2.x - org.gradle.api.internal.file.copy.CopySpecResolver + + if (resolver.metaClass.respondsTo(resolver, 'setIncludeEmptyDirs', boolean)) { + resolver.setIncludeEmptyDirs(true) + } + if (!resolver.metaClass.respondsTo(resolver, 'getDestPath') || + !resolver.metaClass.respondsTo(resolver, 'getSource')) { + throw new RuntimeException("${GradleVersion.current()} is not supported by JEE artifact importer") + } + + final String relativePath = resolver.destPath.pathString + def sourcePaths + + if (resolver.metaClass.respondsTo(resolver, 'getSourcePaths')) { + sourcePaths = resolver.getSourcePaths() + } + else if (resolver.hasProperty('sourcePaths')) { + sourcePaths = resolver.sourcePaths + } + else if (resolver.hasProperty('this$0') && resolver.this$0.metaClass.respondsTo(resolver, 'getSourcePaths')) { + sourcePaths = resolver.this$0.getSourcePaths() + } + else if (resolver.hasProperty('this$0') && resolver.this$0.hasProperty('sourcePaths')) { + sourcePaths = resolver.this$0.sourcePaths + } + + if (sourcePaths) { + (sourcePaths.flatten() as List).each { def path -> + if (path instanceof String) { + visitor.visitSourcePath(relativePath, path) + } + } + } + + resolver.source.visit(new FileVisitor() { + @Override + public void visitDir(FileVisitDetails dirDetails) { + try { + visitor.visitDir(relativePath, dirDetails) + } + catch (Exception ignore) { + } + } + + @Override + public void visitFile(FileVisitDetails fileDetails) { + try { + visitor.visitFile(relativePath, fileDetails) + } + catch (Exception ignore) { + } + } + }) + }) + } +} diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/EarModelBuilderImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/EarModelBuilderImpl.groovy new file mode 100644 index 000000000000..811f509bbee7 --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/EarModelBuilderImpl.groovy @@ -0,0 +1,148 @@ +/* + * Copyright 2000-2015 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.jetbrains.plugins.gradle.tooling.builder + +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.FileVisitDetails +import org.gradle.api.java.archives.Manifest +import org.gradle.plugins.ear.Ear +import org.gradle.plugins.ear.EarPlugin +import org.jetbrains.annotations.NotNull +import org.jetbrains.annotations.Nullable +import org.jetbrains.plugins.gradle.model.ear.EarConfiguration +import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder +import org.jetbrains.plugins.gradle.tooling.ModelBuilderService +import org.jetbrains.plugins.gradle.tooling.internal.ear.EarConfigurationImpl +import org.jetbrains.plugins.gradle.tooling.internal.ear.EarModelImpl +import org.jetbrains.plugins.gradle.tooling.internal.ear.EarResourceImpl +import org.jetbrains.plugins.gradle.tooling.util.DependencyResolver +import org.jetbrains.plugins.gradle.tooling.util.DependencyResolverImpl + +/** + * @author Vladislav.Soroka + * @since 11/10/2015 + */ +class EarModelBuilderImpl implements ModelBuilderService { + + private static final String APP_DIR_PROPERTY = "appDirName" + + @Override + public boolean canBuild(String modelName) { + return EarConfiguration.name.equals(modelName) + } + + @Nullable + @Override + public Object buildAll(String modelName, Project project) { + final EarPlugin earPlugin = project.plugins.findPlugin(EarPlugin) + if (earPlugin == null) return null + + final String appDirName = !project.hasProperty(APP_DIR_PROPERTY) ? + "src/main/application" : String.valueOf(project.property(APP_DIR_PROPERTY)) + def earModels = [] + + def deployConfiguration = project.configurations.findByName(EarPlugin.DEPLOY_CONFIGURATION_NAME) + def earlibConfiguration = project.configurations.findByName(EarPlugin.EARLIB_CONFIGURATION_NAME) + + DependencyResolver dependencyResolver = new DependencyResolverImpl(project, false, false, false) + def deployDependencies = dependencyResolver.resolveDependencies(deployConfiguration) + def earlibDependencies = dependencyResolver.resolveDependencies(earlibConfiguration) + def buildDirPath = project.getBuildDir().absolutePath + + project.tasks.each { Task task -> + if (task instanceof Ear) { + final EarModelImpl earModel = new EarModelImpl(task.archiveName, appDirName, task.getLibDirName()) + + final List earResources = [] + final Ear earTask = task as Ear + + try { + new CopySpecWalker().walk(earTask.rootSpec, new CopySpecWalker.Visitor() { + @Override + void visitSourcePath(String relativePath, String path) { + def file = new File(path) + addPath(buildDirPath, earResources, relativePath, "", file.absolute ? file : new File(earTask.project.projectDir, path), deployConfiguration, earlibConfiguration) + } + + @Override + void visitDir(String relativePath, FileVisitDetails dirDetails) { + addPath(buildDirPath, earResources, relativePath, dirDetails.path, dirDetails.file, deployConfiguration, earlibConfiguration) + } + + @Override + void visitFile(String relativePath, FileVisitDetails fileDetails) { + addPath(buildDirPath, earResources, relativePath, fileDetails.path, fileDetails.file, deployConfiguration, earlibConfiguration) + } + }) + } + catch (Exception e) { + ErrorMessageBuilder builderError = getErrorMessageBuilder(project, e); + project.getLogger().error(builderError.build()); + } + + earModel.resources = earResources + + def deploymentDescriptor = earTask.deploymentDescriptor + if(deploymentDescriptor != null) { + def writer = new StringWriter() + deploymentDescriptor.writeTo(writer) + earModel.deploymentDescriptor = writer.toString() + } + + Manifest manifest = earTask.manifest + if (manifest != null) { + def writer = new StringWriter() + manifest.writeTo(writer) + earModel.manifestContent = writer.toString() + } + + earModels.add(earModel) + } + } + + new EarConfigurationImpl(earModels, deployDependencies, earlibDependencies) + } + + @NotNull + @Override + public ErrorMessageBuilder getErrorMessageBuilder(@NotNull Project project, @NotNull Exception e) { + ErrorMessageBuilder.create( + project, e, "JEE project import errors" + ).withDescription("Ear Artifacts may not be configured properly") + } + + private static addPath( + String buildDirPath, + List earResources, + String earRelativePath, + String fileRelativePath, + File file, + Configuration... earConfigurations) { + + if(file.absolutePath.startsWith(buildDirPath)) return + + for (Configuration conf : earConfigurations) { + if (conf.files.contains(file)) return + } + + earRelativePath = earRelativePath == null ? "" : earRelativePath + + EarConfiguration.EarResource earResource = new EarResourceImpl(earRelativePath, fileRelativePath, file) + earResources.add(earResource) + } +} diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/WarModelBuilderImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/WarModelBuilderImpl.groovy index b1db0c0e056f..45abd4e855cb 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/WarModelBuilderImpl.groovy +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/WarModelBuilderImpl.groovy @@ -18,11 +18,9 @@ package org.jetbrains.plugins.gradle.tooling.builder import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.FileVisitDetails -import org.gradle.api.file.FileVisitor import org.gradle.api.java.archives.Manifest import org.gradle.api.plugins.WarPlugin import org.gradle.api.tasks.bundling.War -import org.gradle.util.GradleVersion import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.Nullable import org.jetbrains.plugins.gradle.model.web.WebConfiguration @@ -68,70 +66,27 @@ class WarModelBuilderImpl implements ModelBuilderService { final List webResources = [] final War warTask = task as War warModel.webXml = warTask.webXml - warTask.rootSpec.setIncludeEmptyDirs(true) - try { - warTask.rootSpec.walk({ def resolver -> - // def resolver -> - // in Gradle v1.x - org.gradle.api.internal.file.copy.CopySpecInternal - // in Gradle v2.x - org.gradle.api.internal.file.copy.CopySpecResolver - - if (resolver.metaClass.respondsTo(resolver, 'setIncludeEmptyDirs', boolean)) { - resolver.setIncludeEmptyDirs(true) - } - if (!resolver.metaClass.respondsTo(resolver, 'getDestPath') || - !resolver.metaClass.respondsTo(resolver, 'getSource')) { - throw new RuntimeException("${GradleVersion.current()} is not supported by web artifact importer") + CopySpecWalker.walk(warTask.rootSpec, new CopySpecWalker.Visitor() { + @Override + void visitSourcePath(String relativePath, String path) { + def file = new File(path) + addPath(webResources, relativePath, "", file.absolute ? file : new File(warTask.project.projectDir, path)) } - final String relativePath = resolver.destPath.pathString - def sourcePaths - - if (resolver.metaClass.respondsTo(resolver, 'getSourcePaths')) { - sourcePaths = resolver.getSourcePaths() - } else if (resolver.hasProperty('sourcePaths')) { - sourcePaths = resolver.sourcePaths - } else if (resolver.hasProperty('this$0') && resolver.this$0.metaClass.respondsTo(resolver, 'getSourcePaths')) { - sourcePaths = resolver.this$0.getSourcePaths() - } else if (resolver.hasProperty('this$0') && resolver.this$0.hasProperty('sourcePaths')) { - sourcePaths = resolver.this$0.sourcePaths - } /*else { - throw new RuntimeException("${GradleVersion.current()} is not supported by web artifact importer") - }*/ - - if(sourcePaths) { - (sourcePaths.flatten() as List).each { def path -> - if (path instanceof String) { - def file = new File(path) - addPath(webResources, relativePath, "", file.absolute ? file : new File(warTask.project.projectDir, path)) - } - } + @Override + void visitDir(String relativePath, FileVisitDetails dirDetails) { + addPath(webResources, relativePath, dirDetails.path, dirDetails.file) } - resolver.source.visit(new FileVisitor() { - @Override - public void visitDir(FileVisitDetails dirDetails) { - try { - addPath(webResources, relativePath, dirDetails.path, dirDetails.file) - } - catch (Exception ignore) { - } + @Override + void visitFile(String relativePath, FileVisitDetails fileDetails) { + if (warTask.webXml == null || + !fileDetails.file.canonicalPath.equals(warTask.webXml.canonicalPath)) { + addPath(webResources, relativePath, fileDetails.path, fileDetails.file) } - - @Override - public void visitFile(FileVisitDetails fileDetails) { - try { - if (warTask.webXml == null || - !fileDetails.file.canonicalPath.equals(warTask.webXml.canonicalPath)) { - addPath(webResources, relativePath, fileDetails.path, fileDetails.file) - } - } - catch (Exception ignore) { - } - } - }) + } }) - warModel.classpath = warTask.classpath.files } catch (Exception ignore) { @@ -158,7 +113,7 @@ class WarModelBuilderImpl implements ModelBuilderService { @Override public ErrorMessageBuilder getErrorMessageBuilder(@NotNull Project project, @NotNull Exception e) { ErrorMessageBuilder.create( - project, e, "Web project import errors" + project, e, "JEE project import errors" ).withDescription("Web Facets/Artifacts will not be configured properly") } diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarConfigurationImpl.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarConfigurationImpl.java new file mode 100644 index 000000000000..ebd9242f9be4 --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarConfigurationImpl.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2015 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.jetbrains.plugins.gradle.tooling.internal.ear; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.model.ExternalDependency; +import org.jetbrains.plugins.gradle.model.ear.EarConfiguration; + +import java.util.Collection; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 11/10/2015 + */ +public class EarConfigurationImpl implements EarConfiguration { + + @NotNull + private final List myEarModels; + @NotNull + private final Collection myDeployDependencies; + @NotNull + private final Collection myEarlibDependencies; + + public EarConfigurationImpl(@NotNull List earModels, + @NotNull Collection deployDependencies, + @NotNull Collection earlibDependencies) { + myEarModels = earModels; + myDeployDependencies = deployDependencies; + myEarlibDependencies = earlibDependencies; + } + + @Override + public List getEarModels() { + return myEarModels; + } + + @NotNull + @Override + public Collection getDeployDependencies() { + return myDeployDependencies; + } + + @NotNull + @Override + public Collection getEarlibDependencies() { + return myEarlibDependencies; + } +} diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarModelImpl.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarModelImpl.java new file mode 100644 index 000000000000..41a8c695ec92 --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarModelImpl.java @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2015 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.jetbrains.plugins.gradle.tooling.internal.ear; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.model.ear.EarConfiguration; + +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 11/10/2015 + */ +public class EarModelImpl implements EarConfiguration.EarModel { + @NotNull + private final String myEarName; + private final String myAppDirName; + private final String myLibDirName; + private List myEarResources; + private String myManifestContent; + private String myDeploymentDescriptor; + + public EarModelImpl(@NotNull String name, @NotNull String appDirName, String libDirName) { + myEarName = name; + myAppDirName = appDirName; + myLibDirName = libDirName; + } + + @NotNull + @Override + public String getEarName() { + return myEarName; + } + + @NotNull + @Override + public String getAppDirName() { + return myAppDirName; + } + + @Override + public String getLibDirName() { + return myLibDirName; + } + + @Override + public List getResources() { + return myEarResources; + } + + public void setResources(List earResources) { + myEarResources = earResources; + } + + public void setManifestContent(String manifestContent) { + myManifestContent = manifestContent; + } + + @Override + public String getManifestContent() { + return myManifestContent; + } + + @Override + public String getDeploymentDescriptor() { + return myDeploymentDescriptor; + } + + public void setDeploymentDescriptor(String deploymentDescriptor) { + myDeploymentDescriptor = deploymentDescriptor; + } +} diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarResourceImpl.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarResourceImpl.java new file mode 100644 index 000000000000..37f5277026f0 --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/internal/ear/EarResourceImpl.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2015 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.jetbrains.plugins.gradle.tooling.internal.ear; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.model.ear.EarConfiguration; + +import java.io.File; + +/** + * @author Vladislav.Soroka + * @since 2/10/14 + */ +public class EarResourceImpl implements EarConfiguration.EarResource { + @NotNull + private final String myEarDirectory; + @NotNull + private final String myRelativePath; + @NotNull + private final File file; + + public EarResourceImpl(@NotNull String earDirectory, @NotNull String relativePath, @NotNull File file) { + myEarDirectory = earDirectory; + this.myRelativePath = relativePath; + this.file = file; + } + + @NotNull + @Override + public String getEarDirectory() { + return myEarDirectory; + } + + @NotNull + @Override + public String getRelativePath() { + return myRelativePath; + } + + @NotNull + @Override + public File getFile() { + return file; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EarResourceImpl)) return false; + + EarResourceImpl resource = (EarResourceImpl)o; + if (!file.getPath().equals(resource.file.getPath())) return false; + if (myEarDirectory != resource.myEarDirectory) return false; + if (!myRelativePath.equals(resource.myRelativePath)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myEarDirectory.hashCode(); + result = 31 * result + myRelativePath.hashCode(); + result = 31 * result + file.getPath().hashCode(); + return result; + } + + @Override + public String toString() { + return "EarResource{" + + "earDirectory=" + myEarDirectory + + ", warRelativePath='" + myRelativePath + '\'' + + ", file=" + file + + '}'; + } +} diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy index e1b021a49697..521df59d04af 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy @@ -565,8 +565,9 @@ class DependencyResolverImpl implements DependencyResolver { group: project.group, version: project.version, scope: scope, - projectPath: project.path + projectPath: project.path, ) + projectDependency.projectDependencyArtifacts = it.projectConfiguration.allArtifacts.files.collect {it.path} result.add(projectDependency) } else if (it instanceof Dependency) { @@ -639,6 +640,7 @@ class DependencyResolverImpl implements DependencyResolver { selectionReason: selectionReason, projectPath: componentSelector.projectPath ) + dependency.projectDependencyArtifacts = artifactMap.get(componentResult.moduleVersion).collect {it.file.path} if (componentResult != dependencyResult.from) { dependency.dependencies.addAll( transform(handledDependencyResults, componentResult.dependencies, artifactMap, componentResultsMap, scope)