| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.tools.idea.gradle.dsl.parser; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Lists; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.psi.codeStyle.CodeStyleManager; |
| import com.intellij.util.SystemProperties; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementVisitor; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; |
| |
| import java.util.List; |
| |
| public class GradleBuildFile { |
| @NotNull private final VirtualFile myFile; |
| @NotNull private final Project myProject; |
| @NotNull private final List<DependenciesElement> myDependenciesBlocks = Lists.newArrayList(); |
| |
| // TODO Get the parsers from an extension point. |
| private final List<? extends GradleDslElementParser> myParsers = Lists.newArrayList(new DependenciesElementParser()); |
| |
| @Nullable private PsiFile myPsiFile; |
| |
| @NotNull |
| public static GradleBuildFile parseFile(@NotNull VirtualFile file, @NotNull Project project) { |
| GradleBuildFile buildFile = new GradleBuildFile(file, project); |
| buildFile.reparse(); |
| return buildFile; |
| } |
| |
| private GradleBuildFile(@NotNull VirtualFile file, @NotNull Project project) { |
| myFile = file; |
| myProject = project; |
| } |
| |
| /** |
| * Parses the build.gradle file again. This is a convenience method to avoid calling {@link #parseFile(VirtualFile, Project)} if |
| * an already parsed build.gradle file needs to be parsed again (for example, after making changes to the PSI elements.) |
| */ |
| public void reparse() { |
| ApplicationManager.getApplication().assertIsDispatchThread(); |
| reset(); |
| myPsiFile = PsiManager.getInstance(myProject).findFile(myFile); |
| if (myPsiFile != null) { |
| myPsiFile.acceptChildren(new GroovyPsiElementVisitor(new GroovyElementVisitor() { |
| @Override |
| public void visitMethodCallExpression(GrMethodCallExpression e) { |
| for (GradleDslElementParser parser : myParsers) { |
| // If a parser was able to parse the given PSI element, stop. Otherwise give another parser the chance to parse the PSI element. |
| if (parser.parse(e, GradleBuildFile.this)) { |
| break; |
| } |
| } |
| } |
| })); |
| } |
| } |
| |
| private void reset() { |
| myDependenciesBlocks.clear(); |
| } |
| |
| void add(@NotNull DependenciesElement dependencies) { |
| myDependenciesBlocks.add(dependencies); |
| } |
| |
| @NotNull |
| public ImmutableList<DependenciesElement> getDependenciesBlocksView() { |
| return ImmutableList.copyOf(myDependenciesBlocks); |
| } |
| |
| /** |
| * Adds a new external dependency to the build.gradle file. If there are more than one "dependencies" block, this method will add the new |
| * dependency to the first one. If the build.gradle file does not have a "dependencies" block, this method will create one. |
| * <p> |
| * Check that {@link #hasPsiFile()} returns {@code true} before invoking this method. |
| * </p> |
| * <p> |
| * Please note the new dependency will <b>not</b> be included in |
| * {@link DependenciesElement#getExternalDependenciesView()} (obtained through {@link #getDependenciesBlocksView()}, unless you invoke |
| * {@link GradleBuildFile#reparse()}. |
| * </p> |
| * |
| * @param configurationName the name of the configuration (e.g. "compile", "compileTest", "runtime", etc.) |
| * @param compactNotation the dependency in "compact" notation: "group:name:version:classifier@extension". |
| * @throws AssertionError if this method is invoked and this {@code GradleBuildFile} does not have a {@link PsiFile}. |
| */ |
| public void addExternalDependency(@NotNull String configurationName, @NotNull String compactNotation) { |
| ApplicationManager.getApplication().assertWriteAccessAllowed(); |
| assert myPsiFile != null; |
| if (myDependenciesBlocks.isEmpty()) { |
| // There are no dependency blocks. Add one. |
| GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject); |
| |
| // We need to add line separators, otherwise reformatting won't work. |
| String lineSeparator = SystemProperties.getLineSeparator(); |
| String text = "dependencies {" + lineSeparator + configurationName + " '" + compactNotation + "'" + lineSeparator + "}"; |
| GrExpression expression = factory.createExpressionFromText(text); |
| |
| myPsiFile.add(expression); |
| CodeStyleManager.getInstance(myProject).reformat(expression); |
| } |
| else { |
| DependenciesElement dependenciesBlock = myDependenciesBlocks.get(0); |
| dependenciesBlock.addExternalDependency(configurationName, compactNotation); |
| } |
| } |
| |
| /** |
| * Indicates whether this {@code GradleBuildFile} has an underlying {@link PsiFile}. A {@code PsiFile} is necessary to update the contents |
| * of the build.gradle file. |
| * @return {@code true} if this {@code GradleBuildFile} has a {@code PsiFile}; {@code false} otherwise. |
| */ |
| public boolean hasPsiFile() { |
| return myPsiFile != null; |
| } |
| } |