/* * Copyright 2010 the original author or authors. * * 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.gradle.util; import groovy.lang.Closure; import org.codehaus.groovy.runtime.GeneratedClosure; import org.gradle.api.Action; import org.gradle.api.Nullable; import org.gradle.api.internal.ClosureBackedAction; import org.gradle.api.internal.DynamicObjectUtil; import org.gradle.internal.Actions; import org.gradle.internal.metaobject.ConfigureDelegate; import org.gradle.internal.metaobject.DynamicInvokeResult; import org.gradle.internal.metaobject.DynamicObject; import java.util.Collection; import java.util.Map; import static org.gradle.util.CollectionUtils.toStringList; public class ConfigureUtil { public static <T> T configureByMap(Map<?, ?> properties, T delegate) { if (properties.isEmpty()) { return delegate; } DynamicObject dynamicObject = DynamicObjectUtil.asDynamicObject(delegate); for (Map.Entry<?, ?> entry : properties.entrySet()) { String name = entry.getKey().toString(); Object value = entry.getValue(); DynamicInvokeResult result = dynamicObject.trySetProperty(name, value); if (result.isFound()) { continue; } result = dynamicObject.tryInvokeMethod(name, value); if (!result.isFound()) { throw dynamicObject.setMissingProperty(name); } } return delegate; } public static <T> T configureByMap(Map<?, ?> properties, T delegate, Collection<?> mandatoryKeys) { if (!mandatoryKeys.isEmpty()) { Collection<String> missingKeys = toStringList(mandatoryKeys); missingKeys.removeAll(toStringList(properties.keySet())); if (!missingKeys.isEmpty()) { throw new IncompleteInputException("Input configuration map does not contain following mandatory keys: " + missingKeys, missingKeys); } } return configureByMap(properties, delegate); } public static class IncompleteInputException extends RuntimeException { private final Collection missingKeys; public IncompleteInputException(String message, Collection missingKeys) { super(message); this.missingKeys = missingKeys; } public Collection getMissingKeys() { return missingKeys; } } /** * <p>Configures {@code target} with {@code configureClosure}, via the {@link Configurable} interface if necessary.</p> * * <p>If {@code target} does not implement {@link Configurable} interface, it is set as the delegate of a clone of * {@code configureClosure} with a resolve strategy of {@code DELEGATE_FIRST}.</p> * * <p>If {@code target} does implement the {@link Configurable} interface, the {@code configureClosure} will be passed to * {@code delegate}'s {@link Configurable#configure(Closure)} method.</p> * * @param configureClosure The configuration closure * @param target The object to be configured * @return The delegate param */ public static <T> T configure(@Nullable Closure configureClosure, T target) { if (configureClosure == null) { return target; } if (target instanceof Configurable) { ((Configurable) target).configure(configureClosure); } else { configureTarget(configureClosure, target, new ConfigureDelegate(configureClosure, target)); } return target; } /** * Creates an action that uses the given closure to configure objects of type T. */ public static <T> Action<T> configureUsing(@Nullable final Closure configureClosure) { if (configureClosure == null) { return Actions.doNothing(); } return new Action<T>() { @Override public void execute(T t) { configure(configureClosure, t); } }; } /** * Called from an object's {@link Configurable#configure} method. */ public static <T> T configureSelf(@Nullable Closure configureClosure, T target) { if (configureClosure == null) { return target; } configureTarget(configureClosure, target, new ConfigureDelegate(configureClosure, target)); return target; } /** * Called from an object's {@link Configurable#configure} method. */ public static <T> T configureSelf(@Nullable Closure configureClosure, T target, ConfigureDelegate closureDelegate) { if (configureClosure == null) { return target; } configureTarget(configureClosure, target, closureDelegate); return target; } private static <T> void configureTarget(Closure configureClosure, T target, ConfigureDelegate closureDelegate) { if (!(configureClosure instanceof GeneratedClosure)) { new ClosureBackedAction<T>(configureClosure, Closure.DELEGATE_FIRST, false).execute(target); return; } // Hackery to make closure execution faster, by short-circuiting the expensive property and method lookup on Closure Closure withNewOwner = configureClosure.rehydrate(target, closureDelegate, configureClosure.getThisObject()); new ClosureBackedAction<T>(withNewOwner, Closure.OWNER_ONLY, false).execute(target); } }