/* * Copyright (C) 2013 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.parser; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; import java.util.List; import static com.android.tools.idea.gradle.parser.BuildFileKey.escapeLiteralString; /** * Represents a Maven repository where the Android plugin or library dependencies may be found. */ public class Repository extends BuildFileStatement { public enum Type { MAVEN_CENTRAL("mavenCentral"), MAVEN_LOCAL("mavenLocal"), JCENTER("jcenter"), URL("maven"); private final String myCallName; Type(String callName) { myCallName = callName; } public String getCallName() { return myCallName; } } public final String myUrl; public final Type myType; public static BuildFileStatement parse(@NotNull String s, @NotNull Project project) { String methodCallName = s; if (methodCallName.endsWith("()")) { methodCallName = methodCallName.substring(0, methodCallName.length() - 2); } if (methodCallName.equalsIgnoreCase(Type.MAVEN_CENTRAL.getCallName())) { return new Repository(Type.MAVEN_CENTRAL, null); } else if (methodCallName.equalsIgnoreCase(Type.MAVEN_LOCAL.getCallName())) { return new Repository(Type.MAVEN_LOCAL, null); } else if (methodCallName.equalsIgnoreCase(Type.JCENTER.getCallName())) { return new Repository(Type.JCENTER, null); } else if (methodCallName.equalsIgnoreCase(Type.URL.getCallName())) { return new Repository(Type.URL, s); } else if (s.startsWith("'") && s.endsWith("'")) { return new Repository(Type.URL, s.substring(1, s.length() - 1)); } else if (s.indexOf('.') >= 0 && s.indexOf('{') == -1) { return new Repository(Type.URL, s); } else { return new UnparseableStatement(s, project); } } public Repository(@NotNull Type type, @Nullable String url) { myType = type; myUrl = url; } @NotNull @Override public List<PsiElement> getGroovyElements(@NotNull GroovyPsiElementFactory factory) { String callName = myType.getCallName(); String extraGroovyCode; switch(myType) { case MAVEN_CENTRAL: case MAVEN_LOCAL: case JCENTER: default: extraGroovyCode = "()"; break; case URL: extraGroovyCode = " { url '" + escapeLiteralString(myUrl) + "' }"; break; } return ImmutableList.of((PsiElement)factory.createStatementFromText(callName + extraGroovyCode)); } @Override public String toString() { switch (myType) { case MAVEN_CENTRAL: case MAVEN_LOCAL: case JCENTER: return myType.getCallName(); case URL: return "'" + myUrl + "'"; } return null; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Repository that = (Repository)o; if (myType != that.myType) { return false; } if (myUrl != null ? !myUrl.equals(that.myUrl) : that.myUrl != null) { return false; } return true; } @Override public int hashCode() { int result = myUrl != null ? myUrl.hashCode() : 0; result = 31 * result + (myType != null ? myType.hashCode() : 0); return result; } public static Factory getFactory() { return new Factory(); } public static class Factory extends BuildFileStatementFactory { @Nullable @Override public List<BuildFileStatement> getValues(@NotNull PsiElement statement) { if (!(statement instanceof GrMethodCall)) { return getUnparseableStatements(statement); } GrMethodCall methodCall = (GrMethodCall)statement; List<BuildFileStatement> list = Lists.newArrayList(); String callName = GradleGroovyFile.getMethodCallName(methodCall); if (Type.MAVEN_CENTRAL.getCallName().equals(callName)) { list.add(new Repository(Type.MAVEN_CENTRAL, null)); } else if (Type.MAVEN_LOCAL.getCallName().equals(callName)) { list.add(new Repository(Type.MAVEN_LOCAL, null)); } else if (Type.JCENTER.getCallName().equals(callName)) { list.add(new Repository(Type.JCENTER, null)); } else if (Type.URL.getCallName().equals(callName)) { // Handles repositories of the form maven('www.foo.com', 'www.fee.com') Iterable<Object> literals = GradleGroovyFile.getLiteralArgumentValues(methodCall); if (!Iterables.isEmpty(literals)) { for (Object literal : literals) { list.add(new Repository(Type.URL, literal.toString())); } } else { // Handles repositories of the form: // // maven { // url 'www.foo.com' // url 'www.fee.com' // } GrClosableBlock cc = GradleGroovyFile.getMethodClosureArgument(methodCall); if (cc != null) { Iterable<GrMethodCall> methodCalls = GradleGroovyFile.getMethodCalls(cc); Iterable<GrMethodCall> urlMethodCalls = GradleGroovyFile.getMethodCalls(cc, "url"); if (Iterables.size(methodCalls) != Iterables.size(urlMethodCalls)) { // If there's something other than a "url" in there, that can mean something like credentials: // maven { // url 'www.foo.com' // credentials { // username 'user' // password 'password' // } // } // Just punt and treat the statement as unparseable. return getUnparseableStatements(statement); } for (GrMethodCall call : methodCalls) { Iterable<Object> values = GradleGroovyFile.getLiteralArgumentValues(call); for (Object value : values) { list.add(new Repository(Type.URL, value.toString())); } } } } if (list.isEmpty()) { return getUnparseableStatements(statement); } } else { return getUnparseableStatements(statement); } return list; } @NotNull @Override public BuildFileStatement parse(@NotNull String s, Project project) { return Repository.parse(s, project); } } }