/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package Sirius.server.middleware.types;
import Sirius.server.localserver.attribute.MemberAttributeInfo;
import Sirius.server.localserver.attribute.ObjectAttribute;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import org.apache.log4j.Logger;
import org.jdesktop.observablecollections.ObservableCollections;
import org.jdesktop.observablecollections.ObservableList;
import org.jdesktop.observablecollections.ObservableListListener;
import org.openide.util.Lookup;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.cismet.cids.dynamics.CidsBean;
import de.cismet.cids.utils.MetaClassCacheService;
import de.cismet.tools.CurrentStackTrace;
/**
* DOCUMENT ME!
*
* @author hell
* @version $Revision$, $Date$
*/
public class BeanFactory {
//~ Static fields/initializers ---------------------------------------------
private static final Logger LOG = Logger.getLogger(BeanFactory.class);
public static final String CIDS_DYNAMICS_SUPERCLASS = "de.cismet.cids.dynamics.CidsBean"; // NOI18N
private static final BeanFactory instance = new BeanFactory();
//~ Instance fields --------------------------------------------------------
private final Map<String, Class> javaclassCache;
private MetaClassCacheService classCacheService;
//~ Constructors -----------------------------------------------------------
/**
* Creates a new BeanFactory object.
*/
private BeanFactory() {
javaclassCache = new HashMap<String, Class>();
classCacheService = Lookup.getDefault().lookup(MetaClassCacheService.class);
}
//~ Methods ----------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static BeanFactory getInstance() {
return instance;
}
/**
* DOCUMENT ME!
*
* @param ol DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static String createObservableListHash(final ObservableList ol) {
long l = 0;
for (final Object o : ol) {
l += o.hashCode();
}
return Long.toHexString(l);
}
/**
* DOCUMENT ME!
*
* @param cidsbean DOCUMENT ME!
*/
public void changeNullSubObjectsToTemplates(final CidsBean cidsbean) {
final MetaObject metaObject = cidsbean.getMetaObject();
final MetaClass metaClass = metaObject.getMetaClass();
final String domain = metaObject.getDomain();
final ObjectAttribute[] attribs = metaObject.getAttribs();
for (final ObjectAttribute oa : attribs) {
if (oa.isArray()) {
} else if (oa.referencesObject()) {
final Object value = oa.getValue();
if (value == null) {
final MetaClass foreignClass = (MetaClass)classCacheService.getAllClasses(domain)
.get(domain + oa.getMai().getForeignKeyClassId());
final MetaObject emptyInstance = foreignClass.getEmptyInstance();
emptyInstance.setStatus(Sirius.server.localserver.object.Object.TEMPLATE);
} else {
final MetaObject subObject = (MetaObject)value;
changeNullSubObjectsToTemplates(subObject.getBean());
}
}
}
}
/**
* DOCUMENT ME!
*
* @param metaObject DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws Exception DOCUMENT ME!
*/
public CidsBean createBean(final MetaObject metaObject) throws Exception {
// TODO getmetaClass kann null liefern wenn keine Rechte vorhanden sind
final MetaClass mc = metaObject.getMetaClass();
if (mc != null) {
final Class<?> javaClass = mc.getJavaClass();
try {
final CidsBean bean = (CidsBean)javaClass.newInstance();
final ObjectAttribute[] attribs = metaObject.getAttribs();
for (final ObjectAttribute a : attribs) {
final String field = a.getMai().getFieldName().toLowerCase();
Object value = a.getValue();
a.setParentObject(metaObject);
if (value instanceof MetaObject) {
final MetaObject tmpMO = (MetaObject)value;
if (tmpMO.isDummy()) {
// 1-n Beziehung (Array)
final List<CidsBean> arrayElements = new ArrayList();
final ObservableList<CidsBean> observableArrayElements = ObservableCollections
.observableList(
arrayElements);
final ObjectAttribute[] arrayOAs = tmpMO.getAttribs();
for (final ObjectAttribute arrayElementOA : arrayOAs) {
arrayElementOA.setParentObject(tmpMO);
final MetaObject arrayElementMO = (MetaObject)arrayElementOA.getValue();
// In diesem MetaObject gibt es nun genau ein Attribut das als Value ein MetaObject hat
final ObjectAttribute[] arrayElementAttribs = arrayElementMO.getAttribs();
for (final ObjectAttribute targetArrayElement : arrayElementAttribs) {
targetArrayElement.setParentObject(arrayElementMO);
final Object targetArrayElementValObj = targetArrayElement.getValue();
if (targetArrayElementValObj instanceof MetaObject) {
final MetaObject targetMO = (MetaObject)targetArrayElementValObj;
final CidsBean cdBean = targetMO.getBean();
if (cdBean != null) {
cdBean.setBacklinkInformation(field, bean);
observableArrayElements.add(cdBean);
} else {
LOG.warn(
"getBean() delivered null -> could be a possible problem with rights/policy?"); // NOI18N
}
break;
}
}
}
value = observableArrayElements;
addObservableListListener(observableArrayElements, bean, field);
} else {
// 1-1 Beziehung
final CidsBean tmpMOBean = tmpMO.getBean();
value = tmpMOBean;
tmpMOBean.setBacklinkInformation(field, bean);
}
} else if ((value == null) && a.isArray()) {
// lege leeren Vector an, sonst wirds sp?ter zu kompliziert
final List<?> arrayElements = new ArrayList();
final ObservableList observableArrayElements = ObservableCollections.observableList(
arrayElements);
value = observableArrayElements;
addObservableListListener(observableArrayElements, bean, field);
}
bean.setProperty(field, value);
}
// bean.addPropertyChangeListener(metaObject);
bean.setMetaObject(metaObject);
bean.addPropertyChangeListener(bean);
return bean;
} catch (Exception e) {
LOG.fatal("Error in createBean metaclass:" + mc, e); // NOI18N
throw new Exception(
"Error in getBean() (instanceof " // NOI18N
+ javaClass
+ ") of MetaObject:" // NOI18N
+ metaObject.getDebugString(),
e);
}
} else {
LOG.warn("getMetaClass() delivered null -> please check policy/permissions!"); // NOI18N
return null;
}
}
/**
* DOCUMENT ME!
*
* @param toObserve DOCUMENT ME!
* @param bean DOCUMENT ME!
* @param field DOCUMENT ME!
*/
private static void addObservableListListener(final ObservableList<?> toObserve,
final CidsBean bean,
final String field) {
toObserve.addObservableListListener(
new ObservableListListener() {
@Override
public void listElementsAdded(final ObservableList list,
final int index,
final int length) {
bean.listElementsAdded(field, list, index, length);
}
@Override
public void listElementsRemoved(final ObservableList list,
final int index,
final List oldElements) {
bean.listElementsRemoved(field, list, index, oldElements);
}
@Override
public void listElementReplaced(final ObservableList list,
final int index,
final Object oldElement) {
bean.listElementReplaced(field, list, index, oldElement);
}
@Override
public void listElementPropertyChanged(final ObservableList list, final int index) {
bean.listElementPropertyChanged(field, list, index);
}
});
}
/**
* DOCUMENT ME!
*
* @param tableName DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private String createJavaClassnameOutOfTableName(final String tableName) {
final String lowerTableName = tableName.toLowerCase();
return tableName.substring(0, 1) + lowerTableName.substring(1);
}
/**
* DOCUMENT ME!
*
* @param metaClass DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws Exception DOCUMENT ME!
*/
public synchronized Class getJavaClass(final MetaClass metaClass) throws Exception {
final String classname = createJavaClassnameOutOfTableName(metaClass.getTableName());
Class ret = javaclassCache.get(classname);
if (ret == null) {
try {
ret = createJavaClass(metaClass);
} catch (final Exception exception) {
LOG.error("fatal error in creating javaclass", exception);
}
javaclassCache.put(classname, ret); // K?nnte null sein
}
return ret;
}
/**
* DOCUMENT ME!
*
* @param metaClass DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws Exception DOCUMENT ME!
*/
private Class createJavaClass(final MetaClass metaClass) throws Exception {
final String classname = "de.cismet.cids.dynamics." // NOI18N
+ createJavaClassnameOutOfTableName(metaClass.getTableName());
final ClassPool pool = ClassPool.getDefault();
final ClassLoader cl = this.getClass().getClassLoader();
final LoaderClassPath lcp = new LoaderClassPath(cl);
pool.appendClassPath(lcp);
final CtClass ctClass = pool.makeClass(classname);
final CtClass superClass = pool.getCtClass(CIDS_DYNAMICS_SUPERCLASS);
ctClass.setSuperclass(superClass);
final List<MemberAttributeInfo> mais = new ArrayList<MemberAttributeInfo>(
metaClass.getMemberAttributeInfos().values());
final StringBuilder propertyNames = new StringBuilder();
for (final MemberAttributeInfo mai : mais) {
final String fieldname = mai.getFieldName().toLowerCase();
String attributeJavaClassName = mai.getJavaclassname();
if (mai.isArray()) {
attributeJavaClassName = "org.jdesktop.observablecollections.ObservableList"; // NOI18N
} else if (mai.isForeignKey()) {
if (attributeJavaClassName.equals("org.postgis.PGgeometry")) { // NOI18N
attributeJavaClassName = "com.vividsolutions.jts.geom.Geometry"; // NOI18N
} else {
attributeJavaClassName = CIDS_DYNAMICS_SUPERCLASS;
}
}
try {
addPropertyToCtClass(pool, ctClass, Class.forName(attributeJavaClassName), fieldname);
if (propertyNames.length() > 0) {
propertyNames.append(", "); // NOI18N
}
propertyNames.append("\"").append(fieldname).append("\""); // NOI18N
} catch (final Exception e) {
LOG.warn("Could not add " + fieldname, e); // NOI18N
}
}
// FIXME: immutable collection instead of possible mutable (-> corrputable) array?
// empty array initialiser causes compile error (new Object[] {})
final CtField propertyNamesStaticField;
if (propertyNames.length() == 0) {
propertyNamesStaticField = CtField.make(
"private String[] PROPERTY_NAMES = new String[0];", // NOI18N
ctClass);
} else {
propertyNamesStaticField = CtField.make(
"private String[] PROPERTY_NAMES = new String[]{" // NOI18N
+ propertyNames
+ "};", // NOI18N
ctClass);
}
final CtMethod propertyNamesGetter = CtNewMethod.getter("getPropertyNames", propertyNamesStaticField); // NOI18N
ctClass.addField(propertyNamesStaticField);
ctClass.addMethod(propertyNamesGetter);
final ProtectionDomain pd = this.getClass().getProtectionDomain();
final Class ret = ctClass.toClass(getClass().getClassLoader(), pd);
if (LOG.isInfoEnabled()) {
LOG.info("Class " + ret + " was successfully created", new CurrentStackTrace()); // NOI18N
}
return ret;
}
/**
* DOCUMENT ME!
*
* @param pool DOCUMENT ME!
* @param ctClass DOCUMENT ME!
* @param propertyType DOCUMENT ME!
* @param propertyName DOCUMENT ME!
*
* @throws Exception DOCUMENT ME!
*/
private static void addPropertyToCtClass(final ClassPool pool,
final CtClass ctClass,
final Class propertyType,
final String propertyName) throws Exception {
final CtField f = new CtField(pool.get(propertyType.getCanonicalName()), propertyName, ctClass);
ctClass.addField(f);
final String fieldname = f.getName();
String getterPrefix = null;
final String postfix = fieldname.toUpperCase().substring(0, 1) + fieldname.substring(1);
if ((propertyType != boolean.class) && (propertyType != Boolean.class)) {
getterPrefix = "get"; // NOI18N
} else {
// Hier wird ein zusaetzlicher "getter" angelegt
getterPrefix = "is"; // NOI18N
final CtMethod additionalGetter = CtNewMethod.getter(getterPrefix + postfix, f);
ctClass.addMethod(additionalGetter);
// leider reicht dieser "getter" nicht. beans binding braucht auch bei einem Boolean ein "getter" der mit
// get anfaengt
getterPrefix = "get"; // NOI18N
}
final String getterName = getterPrefix + postfix;
final String setterName = "set" + postfix; // NOI18N
final CtMethod getter = CtNewMethod.getter(getterName, f);
final CtMethod setter = CtNewMethod.setter(setterName, f);
setter.insertAfter(
"propertyChangeSupport.firePropertyChange(\"" // NOI18N
+ f.getName()
+ "\", null, " // NOI18N
+ f.getName()
+ ");"); // NOI18N
ctClass.addMethod(getter);
ctClass.addMethod(setter);
// Idee falls man oldValue benoetigt: erzeuge den setter wie oben jedoch mit einem anderen Namen (z.b::
// stealthySetVorname) und setze den modifier auf private oder protected in dieser methode wird NICHT der
// propertyChangesupport aufgerufen in einer zus?tzlichen Methoden setVorname die komplett impl. wird kann man
// dann auf den noch nicht ver?nderten Wert zugreifen und oldvalue setzen diese Methode ruft dann die Metjode
// stealthy... auf
}
}