001/**
002 * Copyright 2005-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krms.api.engine;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.mo.ModelObjectComplete;
020
021import java.io.Serializable;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.Map;
025
026/**
027 * <p>Parameter object for the {@link org.kuali.rice.krms.api.engine.Engine} used to pass in mappings from Term to value
028 * (aka facts).  In rule parlance, a fact is a concrete value of a term.  Intuitively this relationship is one of
029 * definition and instance, similar to a parameter definition (e.g. int count) for a function (or method) in a
030 * programming language and a parameter value (e.g. 5).</p>
031 *
032 * <p>{@link Facts} is immutable, and has a private constructor.  Use the inner {@link Builder} class to construct.</p>
033 *
034 * @author Kuali Rice Team (rice.collab@kuali.org)
035 */
036public final class Facts implements ModelObjectComplete, Serializable {
037
038    private static final long serialVersionUID = -1448089944850300846L;
039
040    /**
041     * empty facts object
042     */
043    public static final Facts EMPTY_FACTS = new Facts(Builder.create());
044
045    private Map<Term, Object> factMap;
046
047    private Facts() {
048        // private no-args constructor forces use of builder
049    }
050
051    private Facts(Builder b) {
052        // copy the map to avoid surprises
053        this.factMap = new HashMap<Term, Object>(b.factMap);
054    }
055
056    /**
057     * @return the Map of Terms to fact values.  May be empty, will never be null.  The returned map is unmodifiable.
058     */
059    public Map<Term, Object> getFactMap() {
060        return Collections.unmodifiableMap(factMap);
061    }
062
063    @Override
064    public boolean equals(Object o) {
065        if (this == o) {
066            return true;
067        }
068        if (o == null || getClass() != o.getClass()) {
069            return false;
070        }
071
072        final Facts facts = (Facts) o;
073
074        if (factMap != null ? !factMap.equals(facts.factMap) : facts.factMap != null) {
075            return false;
076        }
077
078        return true;
079    }
080
081    @Override
082    public int hashCode() {
083        return factMap != null ? factMap.hashCode() : 0;
084    }
085
086    @Override
087    public String toString() {
088        return "Facts{" + "factMap=" + factMap + '}';
089    }
090
091    /**
092     *    Builder for a {@link Facts} parameter object
093     */
094    public static class Builder {
095        private Map<Term, Object> factMap = new HashMap<Term, Object>();
096
097        private Builder() {
098            // private constructor forces use of static factory
099        }
100
101        /**
102         * Static factory method to produce instances of this {@link Builder} class
103         * @return
104         */
105        public static Builder create() {
106            return new Builder();
107        }
108
109        /**
110         * Add a fact mapping from the name and parameter map combination to the fact value
111         * @param termName the name of the term.  Must not be empty or null.
112         * @param termParameters any parameters for the term.  May be null or empty.
113         * @param factValue the concrete value for the term
114         */
115        public Builder addFact(String termName, Map<String, String> termParameters, Object factValue) {
116            if (StringUtils.isEmpty(termName)) {
117                throw new IllegalArgumentException("termName must not be null or empty");
118            }
119            factMap.put(new Term(termName, termParameters), factValue);
120            return this;
121        }
122
123        /**
124         * Add a fact mapping from the term name to the fact value
125         * @param termName the name of the term.  Must not be empty or null.
126         * @param factValue the concrete value for the term
127         */
128        public Builder addFact(String termName, Object factValue) {
129            addFact(termName, null, factValue);
130            return this;
131        }
132
133        /**
134         * Add a fact mapping from the {@link Term} to the fact value
135         * @param term the term that this fact is a value for.  Must not be null.
136         * @param factValue the fact value
137         */
138        public Builder addFact(Term term, Object factValue) {
139            if (term == null) {
140                throw new IllegalArgumentException("term must not be null");
141            }
142            factMap.put(term, factValue);
143            return this;
144        }
145
146        /**
147         * Add facts in bulk to this Facts parameter object
148         * @param facts the map of Terms to fact values.  May be null, in that case this call is a no op.
149         */
150        public Builder addFactsByTerm(Map<Term, Object> facts) {
151            if (facts != null) {
152                factMap.putAll(facts);
153            }
154            return this;
155        }
156
157        /**
158         * Add facts in bulk to this Facts parameter object
159         * @param facts the map of term names to fact values.  May be null, in that case this call is a no op.
160         */
161        public Builder addFactsByName(Map<String, Object> facts) {
162            if (facts != null) {
163                for (Map.Entry<String, Object> entry : facts.entrySet()) {
164                    factMap.put(new Term(entry.getKey()), entry.getValue());
165                }
166            }
167            return this;
168        }
169
170        /**
171         * return a {@link Facts} parameter object spawned from this {@link Builder}
172         */
173        public Facts build() {
174            if (factMap.isEmpty()) {
175                return EMPTY_FACTS;
176            } else {
177                return new Facts(this);
178            }
179        }
180
181    }
182
183
184}