/** * $Id: EntityDataUtils.java 105077 2012-02-24 22:54:29Z ottenhoff@longsight.com $ * $URL: https://source.sakaiproject.org/svn/entitybroker/trunk/utils/src/java/org/sakaiproject/entitybroker/util/EntityDataUtils.java $ * EntityDataUtils.java - entity-broker - Aug 11, 2008 2:58:03 PM - azeckoski ************************************************************************** * Copyright (c) 2008, 2009 The Sakai Foundation * * Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.sakaiproject.entitybroker.util; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import org.azeckoski.reflectutils.ReflectUtils; import org.azeckoski.reflectutils.exceptions.FieldnameNotFoundException; import org.sakaiproject.entitybroker.EntityReference; import org.sakaiproject.entitybroker.entityprovider.annotations.EntityId; import org.sakaiproject.entitybroker.entityprovider.capabilities.CollectionResolvable; import org.sakaiproject.entitybroker.entityprovider.extension.ActionReturn; import org.sakaiproject.entitybroker.entityprovider.extension.EntityData; import org.sakaiproject.entitybroker.entityprovider.search.Restriction; import org.sakaiproject.entitybroker.entityprovider.search.Search; /** * Utilities which are useful when working with {@link EntityData} objects and their properties * * @author Aaron Zeckoski (azeckoski @ gmail.com) */ public class EntityDataUtils { /** * Convert a list of objects to entity data objects (DOES NOT populate them), * will preserve null (i.e. null in => null out) */ public static List<EntityData> convertToEntityData(List<?> entities, EntityReference ref) { List<EntityData> l = null; if (entities != null) { l = new ArrayList<EntityData>(); for (Object entity : entities) { l.add( makeEntityData(ref, entity) ); } } return l; } /** * Convert a single object to an entity data object (DOES NOT populate it), * will preserve null (i.e. null in => null out) */ public static EntityData convertToEntityData(Object entity, EntityReference ref) { EntityData ed = null; if (entity != null) { ed = makeEntityData(ref, entity); } return ed; } /** * Convert a list of entities / entity data to just entities, * will preserve null (i.e. null in => null out) */ public static List<?> convertToEntities(List<?> entities) { List<Object> l = null; if (entities != null) { l = new ArrayList<Object>(); for (Object object : entities) { if (object != null) { l.add( EntityDataUtils.convertToEntity(object) ); } } } return l; } /** * Convert an entity/data to just an entity, * will preserve null (i.e. null in => null out) */ public static Object convertToEntity(Object object) { Object togo = null; if (object != null) { if (EntityData.class.isAssignableFrom(object.getClass())) { EntityData ed = (EntityData)object; if (ed.getData() != null) { togo = ed.getData(); } else { togo = ed; } } else { togo = object; } } return togo; } /** * Make an entity data object out of whatever entity is given, * use the given reference, if there is no id then this will attempt to get one otherwise it will use prefix only */ public static EntityData makeEntityData(EntityReference ref, Object entity) { EntityData ed = null; if (entity != null) { if (ref == null) { throw new IllegalArgumentException("ref must not be null or no entity data object can be created"); } Class<?> resultClass = entity.getClass(); if (EntityData.class.isAssignableFrom(resultClass)) { ed = (EntityData) entity; } else { if (ref.getId() == null) { // attempt to get the id if it was not provided String entityId = getEntityId(entity); if (entityId != null) { ref = new EntityReference(ref.getPrefix(), entityId); } } Object entityObject = entity; if (ActionReturn.class.isAssignableFrom(resultClass)) { ActionReturn ar = (ActionReturn) entity; if (ar.entityData == null) { // make entity data from AR if (ar.outputString != null) { entityObject = ar.outputString; } else if (ar.output != null) { entityObject = ar.output; } } else { ed = ar.entityData; } // removed because it makes a mess of the output, maybe add this in later though -AZ // } else if (Map.class.isAssignableFrom(resultClass)) { // props = EntityDataUtils.extractMapProperties((Map)entity); } if (ed == null) { ed = new EntityData(ref, null, entityObject, null); } } } return ed; } /** * Gets the id field value from an entity if possible * @param entity any entity object * @return the id value OR null if it cannot be found */ public static String getEntityId(Object entity) { String entityId = null; try { entityId = ReflectUtils.getInstance().getFieldValueAsString(entity, "entityId", EntityId.class); } catch (FieldnameNotFoundException e) { try { // try just id only as well entityId = ReflectUtils.getInstance().getFieldValueAsString(entity, "id", null); } catch (FieldnameNotFoundException e1) { entityId = null; } } return entityId; } /** * Gets the fieldname of the identifier field for an entity class type * @param type any entity class type * @return the name of the identifier field for this entity OR null if it cannot be determined */ public static String getEntityIdField(Class<?> type) { String entityIdField = ReflectUtils.getInstance().getFieldNameWithAnnotation(type, EntityId.class); if (entityIdField == null) { try { ReflectUtils.getInstance().getFieldType(type, "id"); entityIdField = "id"; } catch (FieldnameNotFoundException e) { entityIdField = null; } } return entityIdField; } /** * Translate the search into one using the standard search params * @param search * @return the translated search */ public static Search translateStandardSearch(Search search) { Search togo = search; if (search == null) { togo = new Search(); } else { EntityDataUtils.translateSearchReference(search, CollectionResolvable.SEARCH_USER_REFERENCE, new String[] {"userReference","userId","userEid","user"}, "/user/"); EntityDataUtils.translateSearchReference(search, CollectionResolvable.SEARCH_LOCATION_REFERENCE, new String[] {"locationReference","locationId","location","siteReference","siteId","site","groupReference","groupId","group"}, "/site/"); EntityDataUtils.translateSearchReference(search, CollectionResolvable.SEARCH_TAGS, new String[] {"tag","tags"}, ""); } return togo; } /** * Adds in a search restriction based on existing restrictions, * this is ideally setup to convert restrictions into one that the developers expect */ public static boolean translateSearchReference(Search search, String key, String[] keys, String valuePrefix) { boolean added = false; if (search.getRestrictionByProperty(key) == null) { Restriction r = findSearchRestriction(search, key, keys, valuePrefix); if (r != null) { search.addRestriction( r ); } } return added; } /** * Finds if there are any search restrictions with the given properties, if so it returns the Restriction, * the value will have the given prefix appended to it's string value if it does not have it, * the returned restriction will have the key set to the input key, * otherwise returns null */ public static Restriction findSearchRestriction(Search search, String key, String[] keys, String valuePrefix) { Restriction r = search.getRestrictionByProperties(keys); if (r != null) { Object value = r.getValue(); if (valuePrefix != null) { String sval = r.getStringValue(); if (!sval.startsWith(valuePrefix)) { value = valuePrefix + sval; } } r = new Restriction(key, value); } return r; } /** * Finds a map value for a key (or set of keys) if it exists in the map and returns the string value of it * @param map any map with strings as keys * @param keys an array of keys to try to find in order * @return the string value OR null if it could not be found for any of the given keys */ public static String findMapStringValue(Map<String, ?> map, String[] keys) { String value = null; Object val = findMapValue(map, keys); if (val != null) { try { value = val.toString(); } catch (RuntimeException e) { // in case the to string fails value = null; } } return value; } /** * Finds a map value for a key (or set of keys) if it exists in the map and returns the value of it * @param map any map with strings as keys * @param keys an array of keys to try to find in order * @return the value OR null if it could not be found for any of the given keys */ public static Object findMapValue(Map<String, ?> map, String[] keys) { if (map == null || keys == null) { return null; } Object value = null; try { for (int i = 0; i < keys.length; i++) { String key = keys[i]; if (map.containsKey(key)) { Object oVal = map.get(key); if (oVal != null) { value = oVal; break; } } } } catch (RuntimeException e) { // in case the given map is not actually of the right types at runtime value = null; } return value; } /** * Puts the values of 2 maps together such that the values from the newStuff map are added to the * original map only if they are not already in the the original * @param <T> * @param <S> * @param original * @param newStuff */ public static <T,S> void putAllNewInMap(Map<T,S> original, Map<T,S> newStuff) { if (original == null) { throw new IllegalArgumentException("original map cannot be null"); } if (newStuff != null) { if (original.isEmpty()) { original.putAll( newStuff ); } else { for (Entry<T,S> entry : newStuff.entrySet()) { if (original.containsKey(entry.getKey())) { continue; } original.put(entry.getKey(), entry.getValue()); } } } } /** * Get the values from a map and convert them to strings, * nulls pass through * @param m * @return the keys of any map as strings and the values as they were */ @SuppressWarnings("unchecked") public static Map<String, Object> extractMapProperties(Map m) { Map<String, Object> props = null; if (m == null || m.isEmpty()) { props = null; } else { props = new HashMap<String, Object>(); for (Entry entry : (Set<Entry>) m.entrySet()) { Object key = entry.getKey(); if (key != null) { Object value = entry.getValue(); if (value != null) { if (Serializable.class.isAssignableFrom(key.getClass())) { // only use simple types that can be serialized props.put(key.toString(), value); } } } } } return props; } }