/*
* #%L
* BroadleafCommerce Open Admin Platform
* %%
* Copyright (C) 2009 - 2013 Broadleaf Commerce
* %%
* Licensed under the Apache 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.apache.org/licenses/LICENSE-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.
* #L%
*/
package org.broadleafcommerce.openadmin.server.service.persistence.module;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.common.persistence.EntityConfiguration;
import org.broadleafcommerce.common.util.BLCFieldUtils;
import org.broadleafcommerce.common.util.HibernateUtils;
import org.broadleafcommerce.openadmin.server.service.persistence.PersistenceManager;
import org.broadleafcommerce.openadmin.server.service.persistence.PersistenceManagerFactory;
import org.broadleafcommerce.openadmin.server.service.persistence.TargetModeType;
import org.hibernate.SessionFactory;
import org.hibernate.ejb.HibernateEntityManager;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.persistence.EntityManager;
/**
*
* @author jfischer
*
*/
public class FieldManager {
private static final Log LOG = LogFactory.getLog(FieldManager.class);
public static final String MAPFIELDSEPARATOR = "---";
protected EntityConfiguration entityConfiguration;
protected EntityManager entityManager;
protected List<SortableValue> middleFields = new ArrayList<SortableValue>(5);
public FieldManager(EntityConfiguration entityConfiguration, EntityManager entityManager) {
this.entityConfiguration = entityConfiguration;
this.entityManager = entityManager;
}
public static Field getSingleField(Class<?> clazz, String fieldName) throws IllegalStateException {
return BLCFieldUtils.getSingleField(clazz, fieldName);
}
public Field getField(Class<?> clazz, String fieldName) throws IllegalStateException {
PersistenceManager persistenceManager = getPersistenceManager();
SessionFactory sessionFactory = persistenceManager.getDynamicEntityDao().getDynamicDaoHelper().
getSessionFactory((HibernateEntityManager) persistenceManager.getDynamicEntityDao().getStandardEntityManager());
BLCFieldUtils fieldUtils = new BLCFieldUtils(sessionFactory, true, persistenceManager.getDynamicEntityDao().useCache(),
persistenceManager.getDynamicEntityDao().getEjb3ConfigurationDao(), entityConfiguration,
persistenceManager.getDynamicEntityDao().getDynamicDaoHelper());
return fieldUtils.getField(clazz, fieldName);
}
public Object getFieldValue(Object bean, String fieldName) throws IllegalAccessException, FieldNotAvailableException {
StringTokenizer tokens = new StringTokenizer(fieldName, ".");
Class<?> componentClass = bean.getClass();
Field field;
Object value = HibernateUtils.deproxy(bean);
while (tokens.hasMoreTokens()) {
String fieldNamePart = tokens.nextToken();
String mapKey = null;
if (fieldNamePart.contains(FieldManager.MAPFIELDSEPARATOR)) {
mapKey = fieldNamePart.substring(fieldNamePart.indexOf(FieldManager.MAPFIELDSEPARATOR) + FieldManager.MAPFIELDSEPARATOR.length(), fieldNamePart.length());
fieldNamePart = fieldNamePart.substring(0, fieldNamePart.indexOf(FieldManager.MAPFIELDSEPARATOR));
}
field = getSingleField(componentClass, fieldNamePart);
if (field != null) {
field.setAccessible(true);
value = field.get(value);
if (value != null && mapKey != null) {
value = ((Map) value).get(mapKey);
}
if (value != null) {
componentClass = value.getClass();
} else {
break;
}
} else {
throw new FieldNotAvailableException("Unable to find field (" + fieldNamePart + ") on the class (" + componentClass + ")");
}
}
return value;
}
public Object setFieldValue(Object bean, String fieldName, Object newValue) throws IllegalAccessException, InstantiationException {
StringTokenizer tokens = new StringTokenizer(fieldName, ".");
Class<?> componentClass = bean.getClass();
Field field;
bean = HibernateUtils.deproxy(bean);
Object value = bean;
int count = tokens.countTokens();
int j=0;
StringBuilder sb = new StringBuilder();
while (tokens.hasMoreTokens()) {
String fieldNamePart = tokens.nextToken();
sb.append(fieldNamePart);
String mapKey = null;
if (fieldNamePart.contains(FieldManager.MAPFIELDSEPARATOR)) {
mapKey = fieldNamePart.substring(fieldNamePart.indexOf(FieldManager.MAPFIELDSEPARATOR) + FieldManager.MAPFIELDSEPARATOR.length(), fieldNamePart.length());
fieldNamePart = fieldNamePart.substring(0, fieldNamePart.indexOf(FieldManager.MAPFIELDSEPARATOR));
}
field = getSingleField(componentClass, fieldNamePart);
field.setAccessible(true);
if (j == count - 1) {
if (mapKey != null) {
Map map = (Map) field.get(value);
if (newValue == null) {
map.remove(mapKey);
} else {
map.put(mapKey, newValue);
}
} else {
field.set(value, newValue);
}
} else {
Object myValue = field.get(value);
if (myValue != null) {
componentClass = myValue.getClass();
value = myValue;
} else {
//consult the entity configuration manager to see if there is a user
//configured entity for this class
try {
Object newEntity = entityConfiguration.createEntityInstance(field.getType().getName());
SortableValue val = new SortableValue(bean, (Serializable) newEntity, j, sb.toString());
middleFields.add(val);
field.set(value, newEntity);
componentClass = newEntity.getClass();
value = newEntity;
} catch (Exception e) {
//Use the most extended type based on the field type
PersistenceManager persistenceManager = getPersistenceManager();
Class<?>[] entities = persistenceManager.getUpDownInheritance(field.getType());
if (!ArrayUtils.isEmpty(entities)) {
Object newEntity = entities[entities.length-1].newInstance();
SortableValue val = new SortableValue(bean, (Serializable) newEntity, j, sb.toString());
middleFields.add(val);
field.set(value, newEntity);
componentClass = newEntity.getClass();
value = newEntity;
LOG.info("Unable to find a reference to ("+field.getType().getName()+") in the EntityConfigurationManager. " +
"Using the most extended form of this class identified as ("+entities[0].getName()+")");
} else {
//Just use the field type
Object newEntity = field.getType().newInstance();
field.set(value, newEntity);
componentClass = newEntity.getClass();
value = newEntity;
LOG.info("Unable to find a reference to ("+field.getType().getName()+") in the EntityConfigurationManager. " +
"Using the type of this class.");
}
}
}
}
sb.append(".");
j++;
}
return value;
}
public Class<?> getFieldType(Field field) {
//consult the entity configuration manager to see if there is a user
//configured entity for this class
Class<?> response;
try {
response = entityConfiguration.lookupEntityClass(field.getType().getName());
} catch (Exception e) {
//Use the most extended type based on the field type
PersistenceManager persistenceManager = getPersistenceManager();
Class<?>[] entities = persistenceManager.getUpDownInheritance(field.getType());
if (!ArrayUtils.isEmpty(entities)) {
response = entities[entities.length-1];
LOG.info("Unable to find a reference to ("+field.getType().getName()+") in the EntityConfigurationManager. " +
"Using the most extended form of this class identified as ("+entities[0].getName()+")");
} else {
//Just use the field type
response = field.getType();
LOG.info("Unable to find a reference to ("+field.getType().getName()+") in the EntityConfigurationManager. " +
"Using the type of this class.");
}
}
return response;
}
public Map<String, Serializable> persistMiddleEntities() throws InstantiationException, IllegalAccessException {
Map<String, Serializable> persistedEntities = new HashMap<String, Serializable>();
Collections.sort(middleFields);
for (SortableValue val : middleFields) {
Serializable s = entityManager.merge(val.entity);
persistedEntities.put(val.getContainingPropertyName(), s);
setFieldValue(val.getBean(), val.getContainingPropertyName(), s);
}
return persistedEntities;
}
public EntityConfiguration getEntityConfiguration() {
return entityConfiguration;
}
protected PersistenceManager getPersistenceManager() {
PersistenceManager persistenceManager;
try {
persistenceManager = PersistenceManagerFactory.getPersistenceManager();
} catch (IllegalStateException e) {
persistenceManager = PersistenceManagerFactory.getPersistenceManager(TargetModeType.SANDBOX);
}
return persistenceManager;
}
private class SortableValue implements Comparable<SortableValue> {
private Integer pos;
private Serializable entity;
private Class<?> entityClass;
private String containingPropertyName;
private Object bean;
public SortableValue(Object bean, Serializable entity, Integer pos, String containingPropertyName) {
this.bean = bean;
this.entity = entity;
this.pos = pos;
this.entityClass = entity.getClass();
this.containingPropertyName = containingPropertyName;
}
@Override
public int compareTo(SortableValue o) {
return pos.compareTo(o.pos) * -1;
}
public String getContainingPropertyName() {
return containingPropertyName;
}
private Object getBean() {
return bean;
}
@Override
public int hashCode() {
int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + (entityClass == null ? 0 : entityClass.hashCode());
result = prime * result + (pos == null ? 0 : pos.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!getClass().isAssignableFrom(obj.getClass()))
return false;
SortableValue other = (SortableValue) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (entityClass == null) {
if (other.entityClass != null)
return false;
} else if (!entityClass.equals(other.entityClass))
return false;
if (pos == null) {
if (other.pos != null)
return false;
} else if (!pos.equals(other.pos))
return false;
return true;
}
private FieldManager getOuterType() {
return FieldManager.this;
}
}
}