View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krms.api.engine;
17  
18  import java.io.Serializable;
19  import java.util.Collections;
20  import java.util.Comparator;
21  import java.util.Iterator;
22  import java.util.Map;
23  import java.util.Map.Entry;
24  import java.util.TreeMap;
25  
26  import org.springframework.util.CollectionUtils;
27  
28  /**
29   * Identifies a (hopefully) resolvable {@link Term}.  For resolution in the {@link TermResolutionEngine}, The
30   * appropriate {@link TermResolver} will be selected by matching the name and parameters of the {@link Term} with
31   * the output and parameter names of the {@link TermResolver}. 
32   *
33   * @author Kuali Rice Team (rice.collab@kuali.org)
34   */
35  public final class Term implements Comparable<Term> {
36  
37  	private final String name;
38  	
39  	private final Map<String, String> parameters;
40  	
41  	private static final TreeMapComparator<String,String> treeMapComparator = new TreeMapComparator<String, String>();
42  
43      /**
44       * Constructor
45       * @param name of the term
46       */
47  	public Term(String name) {
48  		this(name, null);
49  	}	
50  	
51  	/**
52  	 * This constructs a Term, which is a named piece of data that is usually obtainable
53  	 * through the {@link TermResolutionEngine}
54  	 *
55       * @param name the term name
56       * @param parameters an optional map of properties that may be used to allow a single TermResolver to resolve multiple Terms
57       */
58  	public Term(String name, Map<String, String> parameters) {
59  		this.name = name;
60  		if (parameters == null) {
61  			this.parameters = Collections.emptyMap();
62  		} else {
63  			// using TreeMap for ordered iteration since we're comparable
64  			this.parameters = Collections.unmodifiableMap(new TreeMap<String, String>(parameters));
65  		}
66  	}
67  
68      /**
69       * Return the name of the term
70       * @return name of the term
71       */
72  	public String getName() { return this.name; }
73  	public Map<String, String> getProperties() { return parameters; }
74  
75  	/* (non-Javadoc)
76  	 * @see java.lang.Object#hashCode()
77  	 */
78  	@Override
79  	public int hashCode() {
80  		final int prime = 31;
81  		int result = 1;
82  		result = prime * result + ((name == null) ? 0 : name.hashCode());
83  		result = prime * result + ((parameters == null) ? 0 : parameters.hashCode());
84  		return result;
85  	}
86  
87  	/* (non-Javadoc)
88  	 * @see java.lang.Object#equals(java.lang.Object)
89  	 */
90  	@Override
91  	public boolean equals(Object obj) {
92  		if (this == obj)
93  			return true;
94  		if (obj == null)
95  			return false;
96  		if (getClass() != obj.getClass())
97  			return false;
98  		Term other = (Term) obj;
99  		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 }