001 /**
002 * Copyright 2005-2013 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 */
016 package org.kuali.rice.krms.api.engine;
017
018 import java.io.Serializable;
019 import java.util.Collections;
020 import java.util.Comparator;
021 import java.util.Iterator;
022 import java.util.Map;
023 import java.util.Map.Entry;
024 import java.util.TreeMap;
025
026 import org.springframework.util.CollectionUtils;
027
028 /**
029 * Identifies a (hopefully) resolvable {@link Term}. For resolution in the {@link TermResolutionEngine}, The
030 * appropriate {@link TermResolver} will be selected by matching the name and parameters of the {@link Term} with
031 * the output and parameter names of the {@link TermResolver}.
032 *
033 * @author Kuali Rice Team (rice.collab@kuali.org)
034 */
035 public final class Term implements Comparable<Term> {
036
037 private final String name;
038
039 private final Map<String, String> parameters;
040
041 private static final TreeMapComparator<String,String> treeMapComparator = new TreeMapComparator<String, String>();
042
043 /**
044 * Constructor
045 * @param name of the term
046 */
047 public Term(String name) {
048 this(name, null);
049 }
050
051 /**
052 * This constructs a Term, which is a named piece of data that is usually obtainable
053 * through the {@link TermResolutionEngine}
054 *
055 * @param name the term name
056 * @param parameters an optional map of properties that may be used to allow a single TermResolver to resolve multiple Terms
057 */
058 public Term(String name, Map<String, String> parameters) {
059 this.name = name;
060 if (parameters == null) {
061 this.parameters = Collections.emptyMap();
062 } else {
063 // using TreeMap for ordered iteration since we're comparable
064 this.parameters = Collections.unmodifiableMap(new TreeMap<String, String>(parameters));
065 }
066 }
067
068 /**
069 * Return the name of the term
070 * @return name of the term
071 */
072 public String getName() { return this.name; }
073 public Map<String, String> getProperties() { return parameters; }
074
075 /* (non-Javadoc)
076 * @see java.lang.Object#hashCode()
077 */
078 @Override
079 public int hashCode() {
080 final int prime = 31;
081 int result = 1;
082 result = prime * result + ((name == null) ? 0 : name.hashCode());
083 result = prime * result + ((parameters == null) ? 0 : parameters.hashCode());
084 return result;
085 }
086
087 /* (non-Javadoc)
088 * @see java.lang.Object#equals(java.lang.Object)
089 */
090 @Override
091 public boolean equals(Object obj) {
092 if (this == obj)
093 return true;
094 if (obj == null)
095 return false;
096 if (getClass() != obj.getClass())
097 return false;
098 Term other = (Term) obj;
099 return this.compareTo(other) == 0;
100 }
101
102 @Override
103 public int compareTo(Term o) {
104 if (o == null) return 1;
105 if (this == o) return 0;
106 return (treeMapComparator.compare(this.parameters, o.parameters));
107 }
108
109 /**
110 * Return an unmodifiable Map of parameters specified on this Term.
111 * @return an unmodifiable Map of parameters specified on this Term. Guaranteed non-null.
112 */
113 public Map<String, String> getParameters() {
114 return Collections.unmodifiableMap(parameters);
115 }
116
117 @Override
118 public String toString() {
119 // TODO make this pretty
120 StringBuilder sb = new StringBuilder();
121 if (parameters != null) for (Entry<String,String> parameter : parameters.entrySet()) {
122 sb.append(", ");
123 sb.append(parameter.getKey());
124 sb.append("=");
125 sb.append(parameter.getValue());
126 }
127 return getClass().getSimpleName()+"(["+ name + "]" + sb.toString() + ")";
128 }
129
130 @SuppressWarnings("rawtypes")
131 private static class TreeMapComparator<T extends Comparable, V extends Comparable> implements Comparator<Map<T,V>>, Serializable {
132
133 private static final long serialVersionUID = 1L;
134
135 /**
136 * This overridden method compares two {@link TreeMap}s whose keys and elements are both {@link Comparable}
137 *
138 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
139 */
140 @SuppressWarnings("unchecked")
141 @Override
142 public int compare(Map<T,V> o1, Map<T,V> o2) {
143 if (CollectionUtils.isEmpty(o1)) {
144 if (CollectionUtils.isEmpty(o2)) return 0;
145 return -1;
146 } else if (CollectionUtils.isEmpty(o2)) {
147 return 1;
148 }
149
150 // neither one is empty. Iterate through both.
151
152 Iterator<Entry<T,V>> o1Iter = o1.entrySet().iterator();
153 Iterator<Entry<T,V>> o2Iter = o2.entrySet().iterator();
154
155 while (o1Iter.hasNext() && o2Iter.hasNext()) {
156 Entry<T,V> o1Elem = o1Iter.next();
157 Entry<T,V> o2Elem = o2Iter.next();
158 if (o1Elem == null) {
159 if (o2Elem == null) continue;
160 return -1;
161 }
162
163 T o1ElemKey = o1Elem.getKey();
164 T o2ElemKey = o2Elem.getKey();
165 if (o1ElemKey == null) {
166 if (o2ElemKey != null) return -1;
167 // if they're both null, fall through
168 } else {
169 int elemKeyCompare = o1ElemKey.compareTo(o2ElemKey);
170 if (elemKeyCompare != 0) return elemKeyCompare;
171 }
172
173 V o1ElemValue = o1Elem.getValue();
174 V o2ElemValue = o2Elem.getValue();
175 if (o1ElemValue == null) {
176 if (o2ElemValue != null) return -1;
177 // if they're both null, fall through
178 } else {
179 int elemValueCompare = o1ElemValue.compareTo(o2ElemValue);
180 if (elemValueCompare != 0) return elemValueCompare;
181 }
182 }
183
184 if (o1Iter.hasNext()) return 1;
185 if (o2Iter.hasNext()) return -1;
186 return 0;
187 }
188
189 }
190
191
192 }