org.diffkit.diff.conf.DKMagicPlanBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.diffkit.diff.conf.DKMagicPlanBuilder.java

Source

/**
 * Copyright 2010-2011 Joseph Panico
 *
 * 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.diffkit.diff.conf;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.diffkit.common.DKValidate;
import org.diffkit.db.DKDBConnectionInfo;
import org.diffkit.db.DKDatabase;

/**
 * @author jpanico
 */
public class DKMagicPlanBuilder {

    private static final Class<?>[] NON_CACHEABLE_CLASSES = { DKDBConnectionInfo.class, DKDatabase.class };

    private final DKMagicPlan _providedPlan;
    private final Map<String, Object> _resolutionCache = new HashMap<String, Object>();
    private final Logger _log = LoggerFactory.getLogger(this.getClass());

    public DKMagicPlanBuilder(DKMagicPlan providedPlan_) {
        _providedPlan = providedPlan_;
        DKValidate.notNull(_providedPlan);
    }

    public DKPassthroughPlan build()
            throws IllegalAccessException, InstantiationException, InvocationTargetException {
        DKMagicDependency<DKPassthroughPlan> planDependency = new DKMagicDependency<DKPassthroughPlan>(null, null,
                DKPassthroughPlan.class);
        this.resolve(planDependency);
        return planDependency.getResolution();
    }

    @SuppressWarnings("unchecked")
    private void resolve(DKMagicDependency<?> target_)
            throws IllegalAccessException, InstantiationException, InvocationTargetException {
        _log.debug("target_->{}", target_);
        if (target_ == null)
            return;
        Object cachedResolution = this.getCachedResolution(target_);
        if (cachedResolution != null) {
            if (cachedResolution == ObjectUtils.NULL)
                cachedResolution = null;
            target_.resolve(cachedResolution);
            return;
        }

        Object resolution = null;
        DKMagicPlanRule rule = this.getRule(target_);
        _log.debug("rule->{}", rule);
        if (rule != null) {
            resolution = this.applyRule(rule, target_);
            if (resolution instanceof Class) {
                target_.refine((Class) resolution);
                _log.debug("refined dependency->{}", target_);
                this.resolve(target_);
                return;
            } else {
                target_.resolve(resolution);
                _log.debug("resolved dependency->{}", target_);
            }
        } else {
            DKMagicDependency<?>[] dependencies = null;

            try {
                dependencies = target_.getDependencies();
                _log.debug("dependencies->{}", Arrays.toString(dependencies));
                if (dependencies != null) {
                    for (DKMagicDependency<?> dependency : dependencies)
                        try {
                            this.resolve(dependency);
                        } catch (Exception e_) {
                            throw new RuntimeException(
                                    String.format("Could not resolve automatically; need rule for dependency->%s",
                                            dependency),
                                    e_);
                        }
                }
                resolution = target_.resolve();
            } catch (Exception e_) {
                throw new RuntimeException(
                        String.format("Could not resolve automatically; need rule for dependency->%s", target_),
                        e_);
            }
            _log.debug("resolved dependency->{}", target_);
        }
        _log.debug("dependency->{} resolution->{}", target_, resolution);
        if (resolution == null)
            resolution = ObjectUtils.NULL;
        this.encacheResolution(target_, resolution);
    }

    private Object getCachedResolution(DKMagicDependency<?> target_) {
        _log.debug("key->{} cachedResolution->{}", this.getCacheKey(target_),
                _resolutionCache.get(this.getCacheKey(target_)));
        return _resolutionCache.get(this.getCacheKey(target_));
    }

    private void encacheResolution(DKMagicDependency<?> target_, Object resolution_) {
        if (!this.isCacheable(target_.getTargetClass()))
            return;
        _log.debug("key->{} resolution->{}", this.getCacheKey(target_), resolution_);
        _resolutionCache.put(this.getCacheKey(target_), resolution_);
    }

    private boolean isCacheable(Class<?> targetClass_) {
        if (ArrayUtils.contains(NON_CACHEABLE_CLASSES, targetClass_))
            return false;
        String targetPackageName = ClassUtils.getPackageName(targetClass_);
        if (targetPackageName.startsWith("org.diffkit"))
            return true;
        return false;
    }

    private String getCacheKey(DKMagicDependency<?> target_) {
        return String.format("%s.%s", ClassUtils.getShortClassName(target_.getOriginalTargetClass()),
                target_.getParentConstructorParmName());
    }

    private Object applyRule(DKMagicPlanRule rule_, DKMagicDependency<?> target_) {
        Object resolution = rule_.resolve(target_, _providedPlan);
        _log.debug("resolution->{}", resolution);
        return resolution;
    }

    private DKMagicPlanRule getRule(DKMagicDependency<?> dependency_) {
        List<DKMagicPlanRule> allApplicableRules = this.getRulesApplyingTo(dependency_);
        _log.debug("dependency->{} allApplicableRules->{}", dependency_, allApplicableRules);
        if ((allApplicableRules == null) || (allApplicableRules.isEmpty()))
            return null;

        // find the the exclusive rules that apply to dependency_
        List<DKMagicPlanRule> exclusiveRules = DKMagicPlanRule.getExclusive(allApplicableRules);
        // ensure there is at most 1
        int exclusiveRuleCount = (exclusiveRules == null) ? 0 : exclusiveRules.size();
        if (exclusiveRuleCount > 1)
            throw new RuntimeException(
                    String.format("ambiguous rule set; found more than one rule to handle dependency->%s:%s",
                            dependency_, exclusiveRules));
        if (exclusiveRuleCount == 1)
            return exclusiveRules.get(0);
        // if no exclusive rules, look for non-exclusive rules
        List<DKMagicPlanRule> nonExclusiveRules = DKMagicPlanRule.getNonExclusive(allApplicableRules);
        // ensure there is at most 1
        int nonExclusiveRuleCount = (nonExclusiveRules == null) ? 0 : nonExclusiveRules.size();
        if (nonExclusiveRuleCount > 1)
            throw new RuntimeException(
                    String.format("ambiguous rule set; found more than one rule to handle dependency->%s:%s",
                            dependency_, nonExclusiveRules));
        return nonExclusiveRules.get(0);
    }

    private List<DKMagicPlanRule> getRulesApplyingTo(DKMagicDependency<?> dependency_) {
        List<DKMagicPlanRule> rulesApplying = new ArrayList<DKMagicPlanRule>();
        for (DKMagicPlanRule rule : DKMagicPlanRules.RULES) {
            if (rule.applies(dependency_, _providedPlan)) {
                _log.debug("rule->{} applies to dependency->{}", rule, dependency_);
                rulesApplying.add(rule);
            }
        }
        return rulesApplying;
    }

}