| /* |
| * 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.build.gradle.internal.api; |
| |
| import com.android.annotations.NonNull; |
| import com.android.annotations.Nullable; |
| import com.android.builder.model.BaseConfig; |
| import com.android.builder.model.ClassField; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import groovy.lang.GroovyObject; |
| import groovy.lang.GroovyObjectSupport; |
| import groovy.lang.MissingPropertyException; |
| import java.io.File; |
| import java.util.Collection; |
| import java.util.Map; |
| import org.codehaus.groovy.runtime.DefaultGroovyMethods; |
| |
| /** |
| * Read-only version of the BaseConfig wrapping another BaseConfig. |
| * |
| * <p>In the variant API, it is important that the objects returned by the variants are read-only. |
| * |
| * <p>However, even though the API is defined to use the base interfaces as return type (which all |
| * contain only getters), the dynamics of Groovy makes it easy to actually use the setters of the |
| * implementation classes. |
| * |
| * <p>This wrapper ensures that the returned instance is actually just a strict implementation of the |
| * base interface and is read-only. |
| */ |
| public abstract class ReadOnlyBaseConfig extends GroovyObjectSupport implements BaseConfig { |
| |
| @NonNull |
| private BaseConfig baseConfig; |
| |
| protected ReadOnlyBaseConfig(@NonNull BaseConfig baseConfig) { |
| this.baseConfig = baseConfig; |
| } |
| |
| @NonNull |
| @Override |
| public String getName() { |
| return baseConfig.getName(); |
| } |
| |
| @Nullable |
| @Override |
| public String getApplicationIdSuffix() { |
| return baseConfig.getApplicationIdSuffix(); |
| } |
| |
| @Nullable |
| @Override |
| public String getVersionNameSuffix() { |
| return baseConfig.getVersionNameSuffix(); |
| } |
| |
| @NonNull |
| @Override |
| public Map<String, ClassField> getBuildConfigFields() { |
| // TODO: cache immutable map? |
| return ImmutableMap.copyOf(baseConfig.getBuildConfigFields()); |
| } |
| |
| @NonNull |
| @Override |
| public Map<String, ClassField> getResValues() { |
| return ImmutableMap.copyOf(baseConfig.getResValues()); |
| } |
| |
| @NonNull |
| @Override |
| public Collection<File> getProguardFiles() { |
| return ImmutableList.copyOf(baseConfig.getProguardFiles()); |
| } |
| |
| @NonNull |
| @Override |
| public Collection<File> getConsumerProguardFiles() { |
| return ImmutableList.copyOf(baseConfig.getConsumerProguardFiles()); |
| } |
| |
| @NonNull |
| @Override |
| public Collection<File> getTestProguardFiles() { |
| return ImmutableList.copyOf(baseConfig.getTestProguardFiles()); |
| } |
| |
| @NonNull |
| @Override |
| public Map<String, Object> getManifestPlaceholders() { |
| return ImmutableMap.copyOf(baseConfig.getManifestPlaceholders()); |
| } |
| |
| @Nullable |
| @Override |
| public Boolean getMultiDexEnabled() { |
| return baseConfig.getMultiDexEnabled(); |
| } |
| |
| @Nullable |
| @Override |
| public File getMultiDexKeepFile() { |
| return baseConfig.getMultiDexKeepFile(); |
| } |
| |
| @Nullable |
| @Override |
| public File getMultiDexKeepProguard() { |
| return baseConfig.getMultiDexKeepProguard(); |
| } |
| |
| /** |
| * Some build scripts add dynamic properties to flavors declaration (and others) and expect to |
| * retrieve such properties values through this model. Delegate any property we don't know about |
| * to the {@link BaseConfig} groovy object which hopefully will know about the dynamic property. |
| * |
| * @param name the property name |
| * @return the property value if exists or an exception will be thrown. |
| */ |
| @SuppressWarnings("unused") // This is part of the Groovy method dispatch convention. |
| public Object propertyMissing(final String name) { |
| try { |
| return ((GroovyObject) baseConfig).getProperty(name); |
| } catch (MissingPropertyException e) { |
| // do not leak implementation types, replace the delegate with ourselves in the message |
| throw new MissingPropertyException("Could not find " + name + " on " + this); |
| } |
| |
| } |
| |
| /** |
| * Do not authorize setting dynamic properties values and provide a meaningful error message. |
| */ |
| @SuppressWarnings("unused") // This is part of the Groovy method dispatch convention. |
| public void propertyMissing(String name, Object value) { |
| throw new RuntimeException( |
| String.format( |
| "Cannot set property %s on read-only %s.", |
| name, |
| baseConfig.getClass().getName())); |
| } |
| |
| @SuppressWarnings("unused") // This is part of the Groovy/Gradle method dispatch convention. |
| public boolean hasProperty(String name) { |
| if (DefaultGroovyMethods.hasProperty(this, name) != null) { |
| return true; |
| } else { |
| GroovyObject groovyObject = (GroovyObject) this.baseConfig; |
| // Object _Decorated by Gradle implement hasProperty, so the "usual" Groovy conventions |
| // don't apply. |
| return (Boolean) groovyObject.invokeMethod("hasProperty", name); |
| } |
| } |
| } |