View Javadoc

1   /*
2    * Copyright 2007-2009 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.kim.lookup;
17  
18  import java.util.ArrayList;
19  import java.util.Arrays;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.apache.commons.collections.CollectionUtils;
26  import org.apache.commons.collections.Predicate;
27  import org.apache.commons.lang.StringUtils;
28  import org.kuali.rice.kim.bo.entity.dto.KimEntityInfo;
29  import org.kuali.rice.kim.bo.entity.dto.KimPrincipalInfo;
30  import org.kuali.rice.kim.bo.group.dto.GroupInfo;
31  import org.kuali.rice.kim.bo.impl.RoleImpl;
32  import org.kuali.rice.kim.bo.role.impl.RoleMemberImpl;
33  import org.kuali.rice.kim.bo.types.dto.AttributeSet;
34  import org.kuali.rice.kim.service.KIMServiceLocator;
35  import org.kuali.rice.kim.util.KimConstants;
36  import org.kuali.rice.kns.bo.BusinessObject;
37  import org.kuali.rice.kns.lookup.CollectionIncomplete;
38  import org.kuali.rice.kns.util.KNSPropertyConstants;
39  import org.kuali.rice.kns.web.ui.Field;
40  import org.kuali.rice.kns.web.ui.Row;
41  
42  /**
43   * This is a description of what this class does - bhargavp don't forget to fill this in. 
44   * 
45   * @author Kuali Rice Team (rice.collab@kuali.org)
46   *
47   */
48  public abstract class RoleMemberLookupableHelperServiceImpl extends KimLookupableHelperServiceImpl {
49  
50  	protected static final String DETAIL_CRITERIA = "detailCriteria";
51  	protected static final String WILDCARD = "*";
52      protected static final String TEMPLATE_NAMESPACE_CODE = "template." + KimConstants.UniqueKeyConstants.NAMESPACE_CODE;
53      protected static final String TEMPLATE_NAME = "template.name";
54      protected static final String NAMESPACE_CODE = KimConstants.UniqueKeyConstants.NAMESPACE_CODE;
55      protected static final String NAME = "name";
56      protected static final String GROUP_NAME = KimConstants.UniqueKeyConstants.GROUP_NAME;
57      protected static final String ASSIGNED_TO_PRINCIPAL_NAME = "assignedToPrincipal.principalName";
58      protected static final String ASSIGNED_TO_GROUP_NAMESPACE_CODE = "assignedToGroupNamespaceForLookup";
59      protected static final String ASSIGNED_TO_GROUP_NAME = "assignedToGroup." + KimConstants.UniqueKeyConstants.GROUP_NAME;
60      protected static final String ASSIGNED_TO_NAMESPACE_FOR_LOOKUP = "assignedToRoleNamespaceForLookup";
61      protected static final String ASSIGNED_TO_ROLE_NAME = "assignedToRole." + KimConstants.UniqueKeyConstants.ROLE_NAME;
62      protected static final String ATTRIBUTE_NAME = "attributeName";
63      protected static final String ATTRIBUTE_VALUE = "attributeValue";
64      protected static final String ASSIGNED_TO_ROLE_NAMESPACE_CODE = KimConstants.UniqueKeyConstants.NAMESPACE_CODE;
65      protected static final String ASSIGNED_TO_ROLE_ROLE_NAME = KimConstants.UniqueKeyConstants.ROLE_NAME;
66      protected static final String ASSIGNED_TO_ROLE_MEMBER_ID = "members.memberId";
67      protected static final String DETAIL_OBJECTS_ATTRIBUTE_VALUE = "detailObjects.attributeValue";
68      protected static final String DETAIL_OBJECTS_ATTRIBUTE_NAME = "detailObjects.kimAttribute.attributeName";
69      
70      @Override
71      protected List<? extends BusinessObject> getSearchResultsHelper(Map<String, String> fieldValues, boolean unbounded) {
72      	Map<String, String> searchCriteria = buildRoleSearchCriteria(fieldValues);
73      	if(searchCriteria == null)
74      		return new ArrayList<BusinessObject>();
75          return getMemberSearchResults(fieldValues, unbounded);
76      }
77  
78      protected abstract List<? extends BusinessObject> getMemberSearchResults(Map<String, String> searchCriteria, boolean unbounded);
79      
80      protected Map<String, String> buildSearchCriteria(Map<String, String> fieldValues){
81          String templateNamespaceCode = fieldValues.get(TEMPLATE_NAMESPACE_CODE);
82          String templateName = fieldValues.get(TEMPLATE_NAME);
83          String namespaceCode = fieldValues.get(NAMESPACE_CODE);
84          String name = fieldValues.get(NAME);
85          String attributeDetailValue = fieldValues.get(ATTRIBUTE_VALUE);
86          String attributeDetailName = fieldValues.get(ATTRIBUTE_NAME);
87          String detailCriteria = fieldValues.get( DETAIL_CRITERIA );
88          String active = fieldValues.get( KNSPropertyConstants.ACTIVE );
89  
90      	Map<String,String> searchCriteria = new HashMap<String, String>();
91      	if(StringUtils.isNotEmpty(templateNamespaceCode)) {
92      		searchCriteria.put(TEMPLATE_NAMESPACE_CODE, WILDCARD+templateNamespaceCode+WILDCARD);
93      	}
94          if(StringUtils.isNotEmpty(templateName)) {
95          	searchCriteria.put(TEMPLATE_NAME, WILDCARD+templateName+WILDCARD);
96          }
97          if(StringUtils.isNotEmpty(namespaceCode)) {
98          	searchCriteria.put(NAMESPACE_CODE, WILDCARD+namespaceCode+WILDCARD);
99          }
100         if(StringUtils.isNotEmpty(name)) {
101         	searchCriteria.put(NAME, WILDCARD+name+WILDCARD);
102         }
103         if(StringUtils.isNotEmpty(attributeDetailValue)) {
104         	searchCriteria.put(DETAIL_OBJECTS_ATTRIBUTE_VALUE, WILDCARD+attributeDetailValue+WILDCARD);
105         }
106         if(StringUtils.isNotEmpty(attributeDetailName)) {
107         	searchCriteria.put(DETAIL_OBJECTS_ATTRIBUTE_NAME, WILDCARD+attributeDetailName+WILDCARD);
108         }
109         if ( StringUtils.isNotBlank( detailCriteria ) ) {
110         	searchCriteria.put(DETAIL_CRITERIA, detailCriteria);
111         }
112         if ( StringUtils.isNotBlank( active ) ) {
113         	searchCriteria.put(KNSPropertyConstants.ACTIVE, active);
114         }
115 
116         return searchCriteria;
117     }
118     
119     protected String getQueryString(String parameter){
120     	if(StringUtils.isEmpty(parameter))
121     		return WILDCARD;
122     	else
123     		return WILDCARD+parameter+WILDCARD;
124     }
125     
126     /**
127      * - detail value: 
128      * if this is provided a full (template namespace and template name) or namespace must be supplied 
129      * - may need to do further restrictions once we see how this performs
130      *  
131      * @param fieldValues the values of the query
132      * @see org.kuali.rice.kns.lookup.AbstractLookupableHelperServiceImpl#validateSearchParameters(java.util.Map)
133      */
134     @SuppressWarnings("unchecked")
135     @Override
136     public void validateSearchParameters(Map fieldValues) {
137         super.validateSearchParameters(fieldValues);
138 /*
139         String valueTemplateNamespaceCode = (String) fieldValues.get(TEMPLATE_NAMESPACE_CODE);
140         String valueTemplateName = (String) fieldValues.get(TEMPLATE_NAME);
141         String name = (String) fieldValues.get(NAME);
142         
143         if (!((StringUtils.isNotEmpty(valueTemplateNamespaceCode) && StringUtils.isNotEmpty(valueTemplateName)) 
144         		|| StringUtils.isNotEmpty(name))){
145             throw new ValidationException("For a search to be performed on an attribute detail value, " +
146             		"a combination of template namespace and template name, or a namespace must be supplied");
147         }
148         */
149     }
150 
151     @SuppressWarnings({ "unchecked" })
152 	protected Map<String, String> buildRoleSearchCriteria(Map<String, String> fieldValues){
153        	String assignedToPrincipalName = fieldValues.get(ASSIGNED_TO_PRINCIPAL_NAME);
154     	Map<String, String> searchCriteria;
155     	List<KimPrincipalInfo> principals = new ArrayList<KimPrincipalInfo>();
156         if(StringUtils.isNotEmpty(assignedToPrincipalName)){
157         	searchCriteria = new HashMap<String, String>();
158         	searchCriteria.put("principals.principalName", WILDCARD+assignedToPrincipalName+WILDCARD);
159         	List<KimEntityInfo> kimEntityInfoList = KIMServiceLocator.getIdentityManagementService().lookupEntityInfo(searchCriteria, true);
160         	if(kimEntityInfoList == null || kimEntityInfoList.isEmpty()) {
161         		return null;
162         	}
163         	else {
164         		for (KimEntityInfo kimEntityInfo : kimEntityInfoList) {
165         			if(kimEntityInfo.getPrincipals() != null){
166         				principals.addAll(kimEntityInfo.getPrincipals());
167         			}
168         		}
169         	}
170         }
171         String assignedToGroupNamespaceCode = fieldValues.get(ASSIGNED_TO_GROUP_NAMESPACE_CODE);
172         String assignedToGroupName = fieldValues.get(ASSIGNED_TO_GROUP_NAME);
173         List<GroupInfo> groups = null;
174         if(StringUtils.isNotEmpty(assignedToGroupNamespaceCode) && StringUtils.isEmpty(assignedToGroupName) ||
175         		StringUtils.isEmpty(assignedToGroupNamespaceCode) && StringUtils.isNotEmpty(assignedToGroupName) ||
176         		StringUtils.isNotEmpty(assignedToGroupNamespaceCode) && StringUtils.isNotEmpty(assignedToGroupName)){
177         	searchCriteria = new HashMap<String, String>();
178         	searchCriteria.put(NAMESPACE_CODE, getQueryString(assignedToGroupNamespaceCode));
179         	searchCriteria.put(GROUP_NAME, getQueryString(assignedToGroupName));
180         	groups = (List<GroupInfo>)KIMServiceLocator.getGroupService().lookupGroups(searchCriteria);
181         	if(groups==null || groups.size()==0)
182         		return null;
183         }
184 
185         String assignedToRoleNamespaceCode = fieldValues.get(ASSIGNED_TO_NAMESPACE_FOR_LOOKUP);
186         String assignedToRoleName = fieldValues.get(ASSIGNED_TO_ROLE_NAME);
187 
188     	searchCriteria = new HashMap<String, String>();
189         if(StringUtils.isNotEmpty(assignedToRoleNamespaceCode))
190         	searchCriteria.put(ASSIGNED_TO_ROLE_NAMESPACE_CODE, WILDCARD+assignedToRoleNamespaceCode+WILDCARD);
191         if(StringUtils.isNotEmpty(assignedToRoleName))
192         	searchCriteria.put(ASSIGNED_TO_ROLE_ROLE_NAME, WILDCARD+assignedToRoleName+WILDCARD);
193 
194     	StringBuffer memberQueryString = null;
195         if(principals!=null){
196         	memberQueryString = new StringBuffer();
197         	for(KimPrincipalInfo principal: principals){
198         		memberQueryString.append(principal.getPrincipalId()+KimConstants.KimUIConstants.OR_OPERATOR);
199         	}
200             if(memberQueryString.toString().endsWith(KimConstants.KimUIConstants.OR_OPERATOR))
201             	memberQueryString.delete(memberQueryString.length()-KimConstants.KimUIConstants.OR_OPERATOR.length(), memberQueryString.length());
202         }
203         if(groups!=null){
204         	if(memberQueryString==null)
205         		memberQueryString = new StringBuffer();
206         	else if(StringUtils.isNotEmpty(memberQueryString.toString()))
207         		memberQueryString.append(KimConstants.KimUIConstants.OR_OPERATOR);
208         	for(GroupInfo group: groups){
209         		memberQueryString.append(group.getGroupId()+KimConstants.KimUIConstants.OR_OPERATOR);
210         	}
211             if(memberQueryString.toString().endsWith(KimConstants.KimUIConstants.OR_OPERATOR))
212             	memberQueryString.delete(memberQueryString.length()-KimConstants.KimUIConstants.OR_OPERATOR.length(), memberQueryString.length());
213         	searchCriteria.put(ASSIGNED_TO_ROLE_MEMBER_ID, memberQueryString.toString());
214         }
215         if(memberQueryString!=null && StringUtils.isNotEmpty(memberQueryString.toString()))
216         	searchCriteria.put(ASSIGNED_TO_ROLE_MEMBER_ID, memberQueryString.toString());
217 
218         return searchCriteria;
219     }
220 
221     
222     /** Checks whether the 2nd map is a subset of the first. */
223 	protected boolean isMapSubset( AttributeSet mainMap, AttributeSet subsetMap ) {
224 		for ( Map.Entry<String, String> keyValue : subsetMap.entrySet() ) {
225 			if ( !mainMap.containsKey(keyValue.getKey()) 
226 					|| !StringUtils.equals( mainMap.get(keyValue.getKey()), keyValue.getValue() ) ) {
227 //				if ( LOG.isDebugEnabled() ) {
228 //					LOG.debug( "Maps did not match:\n" + mainMap + "\n" + subsetMap );
229 //				}
230 				return false;
231 			}
232 		}
233 //		if ( LOG.isDebugEnabled() ) {
234 //			LOG.debug( "Maps matched:\n" + mainMap + "\n" + subsetMap );
235 //		}
236 		return true;
237 	}
238 
239 	/** Converts a special criteria string that is in the form key=value,key2=value2 into a map */
240 	protected AttributeSet parseDetailCriteria( String detailCritiera ) {
241 	    if ( StringUtils.isBlank(detailCritiera) ) {
242 	        return new AttributeSet(0);
243 	    }
244 		String[] keyValuePairs = StringUtils.split(detailCritiera, ',');
245 		if ( keyValuePairs == null || keyValuePairs.length == 0 ) {
246 		    return new AttributeSet(0);
247 		}
248 		AttributeSet parsedDetails = new AttributeSet( keyValuePairs.length );
249 		for ( String keyValueStr : keyValuePairs ) {
250 			String[] keyValue = StringUtils.split(keyValueStr, '=');
251 			if ( keyValue.length >= 2 ) {
252 				parsedDetails.put(keyValue[0], keyValue[1]);
253 			}
254 		}
255 		return parsedDetails;
256 	}
257 	
258 	
259 	/**
260 	 * This overridden method ...
261 	 * 
262 	 * @see org.kuali.rice.kns.lookup.AbstractLookupableHelperServiceImpl#getRows()
263 	 */
264 	@Override
265 	public List<Row> getRows() {
266 		List<Row> rows = super.getRows();
267 		Iterator<Row> i = rows.iterator();
268 		while ( i.hasNext() ) {
269 			Row row = i.next();
270 			int numFieldsRemoved = 0;
271 			boolean rowIsBlank = true;
272 			for (Iterator<Field> fieldIter = row.getFields().iterator(); fieldIter.hasNext();) {
273 				Field field = fieldIter.next();
274 				String propertyName = field.getPropertyName();
275 				if ( propertyName.equals(DETAIL_CRITERIA) ) {
276 					Object val = getParameters().get( propertyName );
277 					String propVal = null;
278 					if ( val != null ) {
279 						propVal = (val instanceof String)?(String)val:((String[])val)[0];
280 					}
281 					if ( StringUtils.isBlank( propVal ) ) {
282 						fieldIter.remove();
283 						numFieldsRemoved++;
284 					} else {
285 						field.setReadOnly(true);
286 						rowIsBlank = false;
287 						// leaving this in would prevent the "clear" button from resetting this value
288 //						field.setDefaultValue( propVal );
289 					}
290 				} else if (!Field.BLANK_SPACE.equals(field.getFieldType())) {
291 					rowIsBlank = false;
292 				}
293 			}
294 			// Check if any fields were removed from the row.
295 			if (numFieldsRemoved > 0) {
296 				// If fields were removed, check whether or not the remainder of the row is empty or only has blank space fields.
297 				if (rowIsBlank) {
298 					// If so, then remove the row entirely.
299 					i.remove();
300 				} else {
301 					// Otherwise, add one blank space for each field that was removed, using a technique similar to FieldUtils.createBlankSpace.
302 					// It is safe to just add blank spaces as needed, since the removable field is the last of the visible ones in the list (or
303 					// at least for the Permission and Responsibility lookups).
304 					while (numFieldsRemoved > 0) {
305 						Field blankSpace = new Field();
306 						blankSpace.setFieldType(Field.BLANK_SPACE);
307 						blankSpace.setPropertyName(Field.BLANK_SPACE);
308 						row.getFields().add(blankSpace);
309 						numFieldsRemoved--;
310 					}
311 				}
312 			}
313 		}
314 		return rows;
315 	}
316     
317 	protected Long getActualSizeIfTruncated(List result){
318 		Long actualSizeIfTruncated = new Long(0); 
319 		if(result instanceof CollectionIncomplete)
320 			actualSizeIfTruncated = ((CollectionIncomplete)result).getActualSizeIfTruncated();
321 		return actualSizeIfTruncated;
322 	}
323 	
324 	@SuppressWarnings("unchecked")
325 	protected List<RoleImpl> searchRoles(Map<String, String> roleSearchCriteria, boolean unbounded){
326 		List<RoleImpl> roles = (List<RoleImpl>)getLookupService().findCollectionBySearchHelper(
327 				RoleImpl.class, roleSearchCriteria, unbounded);
328 		String membersCrt = roleSearchCriteria.get("members.memberId");
329 		List<RoleImpl> roles2Remove = new ArrayList<RoleImpl>();
330 		if(StringUtils.isNotBlank(membersCrt)){
331 			List<String> memberSearchIds = new ArrayList<String>();
332 			List<String> memberIds = new ArrayList<String>(); 
333 			if(membersCrt.contains(KimConstants.KimUIConstants.OR_OPERATOR))
334 				memberSearchIds = new ArrayList<String>(Arrays.asList(membersCrt.split("\\|")));
335 			else
336 				memberSearchIds.add(membersCrt);
337 			for(RoleImpl roleImpl : roles){	
338 				List<RoleMemberImpl> roleMembers = roleImpl.getMembers();
339 				memberIds.clear(); 
340 		        CollectionUtils.filter(roleMembers, new Predicate() {
341 					public boolean evaluate(Object object) {
342 						RoleMemberImpl member = (RoleMemberImpl) object;
343 						// keep active member
344 						return member.isActive();
345 					}
346 				});
347 		       
348 		        if(roleMembers != null && !roleMembers.isEmpty()){
349 		        	for(RoleMemberImpl memberImpl : roleMembers)
350 		        		memberIds.add(memberImpl.getMemberId());
351 		        	if(((List<String>)CollectionUtils.intersection(memberSearchIds, memberIds)).isEmpty())
352 		        		roles2Remove.add(roleImpl);
353 		        }
354 		        else
355 		        {
356 		        	roles2Remove.add(roleImpl);
357 		        }
358 			}
359 		}
360 		if(!roles2Remove.isEmpty())
361 			roles.removeAll(roles2Remove);
362 		return roles;
363 	}
364 
365 
366 }