/**
* Copyright (C) 2001-2004 France Telecom R&D
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.objectweb.speedo.naming.lib;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import org.objectweb.asm.Type;
import org.objectweb.jorm.api.PException;
import org.objectweb.jorm.metainfo.api.Class;
import org.objectweb.jorm.metainfo.api.ClassMapping;
import org.objectweb.jorm.metainfo.api.ClassRef;
import org.objectweb.jorm.metainfo.api.CompositeName;
import org.objectweb.jorm.metainfo.api.GenClassMapping;
import org.objectweb.jorm.metainfo.api.GenClassRef;
import org.objectweb.jorm.metainfo.api.Manager;
import org.objectweb.jorm.metainfo.api.MetaObject;
import org.objectweb.jorm.metainfo.api.NameDef;
import org.objectweb.jorm.metainfo.api.NameRef;
import org.objectweb.jorm.naming.api.PName;
import org.objectweb.jorm.naming.api.PNameCoder;
import org.objectweb.jorm.type.api.PType;
import org.objectweb.medor.filter.lib.ExpressionPrinter;
import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.api.SpeedoRuntimeException;
import org.objectweb.speedo.generation.enhancer.pc.POVariableNames;
import org.objectweb.speedo.generation.jorm.JormMIMappingBuilder;
import org.objectweb.speedo.generation.lib.NamingRules;
import org.objectweb.speedo.mapper.api.JormFactory;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.metadata.SpeedoField;
import org.objectweb.speedo.metadata.SpeedoIdentity;
import org.objectweb.speedo.naming.api.MIBuilderHelper;
import org.objectweb.speedo.naming.api.NamingManager;
import org.objectweb.speedo.naming.api.UserId;
import org.objectweb.speedo.naming.api.UserIdFactory;
import org.objectweb.util.monolog.api.BasicLevel;
/**
*
* @author S.Chassande-Barrioz
*/
public class UserIdCompositeNamingManager
extends NamingManagerHelper
implements NamingManager {
private final static String COMPOSITE_USER_ID = "cuid";
protected String getName() {
return COMPOSITE_USER_ID;
}
// IMPLEMENTATION OF THE METHOD FROM THE NamingManager INTERFACE //
//---------------------------------------------------------------//
public boolean canManage(SpeedoClass sc) {
if (sc.identity.strategy == SpeedoIdentity.USER_ID) {
String objectidClass = sc.identity.objectidClass;
if (objectidClass == null && sc.generateObjectId()) {
objectidClass = NamingRules.generatedObjectIdName(sc.getFQName());
}
return objectidClass != null;
}
return false;
}
public Object encode(PName pn) throws PException {
if (pn instanceof UserIdFactory) {
return ((UserIdFactory) pn).getNewUserId();
}
return null;
}
public PName decode(PNameCoder pnc,
Object oid,
java.lang.Class clazz,
JormFactory jf) throws PException {
if (oid instanceof UserId) {
//the oid is a UserId
if (pnc != null) {
//Use the pnc found by the clazz
return pnc.decodeAbstract(oid, null);
} else if (((UserId) oid).speedoGetPersistentClassName() != null) {
//No pnc but the class name is inside the UserId
ClassLoader cl = oid.getClass().getClassLoader();
if (cl == null) {
cl = jf.getClass().getClassLoader();
}
if (cl == null) {
cl = ClassLoader.getSystemClassLoader();
}
return jf.getPNamingContext(
((UserId) oid).speedoGetPersistentClassName(), cl)
.decodeAbstract(oid, null);
}
} else if (pnc != null
&& (oid instanceof String)
&& (pnc.getNull() instanceof UserIdFactory)) {
//The oid is a String which must be the parameter of the constructor
java.lang.Class idclass = ((UserIdFactory) pnc.getNull())
.getNewUserId().getClass();
Object o = null;
try {
o = idclass.getConstructor(new java.lang.Class[]{String.class})
.newInstance(new Object[]{(String) oid});
} catch (Exception e) {
throw new SpeedoRuntimeException("No constructor with a String " +
"parameter available on the identifier class: "
+ idclass.getName());
}
//recall with the UserId
return decode(pnc, o, clazz, jf);
}
return null;
}
public boolean canProvidePBinder(Object hints, ClassLoader classLoader) {
if(!super.canProvidePBinder(hints, classLoader)) {
return false;
}
String cn = getBinderClassNameFromHints(hints, COMPOSITE_USER_ID);
if (cn == null) {
return false;
}
try {
classLoader.loadClass(cn);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
public boolean canProvidePNamingContext(Object hints, ClassLoader classLoader) {
if(!super.canProvidePNamingContext(hints, classLoader)) {
return false;
}
String cn = getPNCClassNameFromHints(hints, COMPOSITE_USER_ID);
if (cn == null) {
return false;
}
try {
classLoader.loadClass(getPNCClassNameFromHints(hints, COMPOSITE_USER_ID));
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
public void defineClassIdentifierNameDef(NameDef nd,
Class jc,
SpeedoClass sc,
ClassMapping cm,
MIBuilderHelper mibh,
JormMIMappingBuilder mb,
Collection createdMOs) throws SpeedoException, PException {
boolean debug = logger.isLoggable(BasicLevel.DEBUG);
if (debug) {
logger.log(BasicLevel.DEBUG,
"CompositeUserId: Filling the identifider name def: \n\t class="
+ sc.getFQName());
}
SpeedoClass clazzWithIdField = sc.getAncestor();
if (clazzWithIdField == null) {
clazzWithIdField = sc;
}
List idFields = clazzWithIdField.getPKFields();
String objectidClass = clazzWithIdField.identity.objectidClass;
if (objectidClass == null && sc.generateObjectId()) {
objectidClass = NamingRules.generatedObjectIdName(clazzWithIdField.getFQName());
clazzWithIdField.identity.oidClassAutoCalculated = true;
clazzWithIdField.identity.objectidClass = objectidClass;
}
Manager manager = (Manager) jc.getPackage().getParent();
//The user has specified an object id ==> use it for the namedef
CompositeName cn = manager.getCompositeName(objectidClass);
boolean cnNotDefined = (cn == null);
if (cnNotDefined) {
if (debug) {
logger.log(BasicLevel.DEBUG, "\tCreate the composite name " + objectidClass);
}
cn = manager.createCompositeName(objectidClass);
createdMOs.add(cn);
}
NameRef nr = nd.createNameRef(cn);
for (int i = 0; i < idFields.size(); i++) {
SpeedoField pkField = (SpeedoField) idFields.get(i);
int size = PType.NOSIZE;
int scale = PType.NOSIZE;
if (pkField.columns != null && pkField.columns.length == 1) {
if (pkField.columns[0].length != -1) {
size = pkField.columns[0].length;
}
if (pkField.columns[0].scale!= -1) {
scale = pkField.columns[0].scale;
}
}
if (cnNotDefined) {
cn.createCompositeNameField(pkField.name,
mibh.getPrimitivePType(Type.getType(pkField.type)),
size, scale);
}
nr.addProjection(pkField.name, pkField.name);
}
}
public boolean needInheritanceDiscriminator(SpeedoClass sc)
throws SpeedoException {
return true;
}
/**
* This internal method defines a NameDef based on on the PK fields of the
* Referenced class. This method manages ClassRef owned by a class or by
* a generic class. But this method does not mapped the fields (ex:
* no rdb mapping)
* @param nd is the namedef to fill
* @param cr is the JORM meta object representing the reference. This
* ClassRef can be owned by a Class or a GenClassRef.
* @param sf is the Speedo MetaObject representing the persistent field
* linked to this ClassRef. If the owner of the ClassRef is a simple class
* then this parameter represents the classRef too. If the owner of the
* ClassRef is a generic class, then this parameter represents the reference
* to the generic class (Collection, Map, ...).
* @param mibh is a helper for the management of the JORM meta info
* @param prefix is a prefix for the name of the field composing the
* identifier
*/
private void defineClassReferenceNameDef(NameDef nd,
ClassRef cr,
SpeedoField sf,
MIBuilderHelper mibh,
String prefix) throws SpeedoException, PException {
Manager manager = mibh.getManager(cr);
SpeedoClass referencedClass = sf.moClass.getSpeedoClassFromContext(
cr.getMOClass().getFQName());
SpeedoClass clazzWithIdField = referencedClass.getAncestor();
if (clazzWithIdField == null) {
clazzWithIdField = referencedClass;
}
CompositeName cn = manager.getCompositeName(clazzWithIdField.identity.objectidClass);
NameRef nr = nd.createNameRef(cn);
List idFields = clazzWithIdField.getPKFields();
for (int i = 0; i < idFields.size(); i++) {
SpeedoField pkField = (SpeedoField) idFields.get(i);
int size = PType.NOSIZE;
int scale = PType.NOSIZE;
if (pkField.columns != null && pkField.columns.length == 1) {
if (pkField.columns[0].length != -1) {
size = pkField.columns[0].length;
}
if (pkField.columns[0].scale != -1) {
scale = pkField.columns[0].scale;
}
}
// create a hidden field in the class corresponding to this
// composite Name field. The name is based on a prefix (ClassRef name)
String fieldName = prefix + pkField.name;
PType type = mibh.getPrimitivePType(Type.getType(pkField.type));
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "\tField: name=" + fieldName
+ ", type=" + type.getJavaName());
}
mibh.createNameDefField(cr.getParent(), fieldName, type, size, scale);
//define the projection between composite name fields and field used
// in NameRef
nr.addProjection(pkField.name, fieldName);
}
}
public void defineClassReferenceNameDef(NameDef nd,
ClassRef cr,
SpeedoField sf,
SpeedoClass currentClass,
ClassMapping cm,
MIBuilderHelper mibh,
JormMIMappingBuilder mb) throws SpeedoException, PException {
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "CompositeUserId: Filling the name def of a ClassRef from a class: "
+ "\n\tField: " + currentClass.getFQName() + "#" + sf.name
+ "\n\ttarget class=" + sf.getClassName());
}
String prefix = mibh.getNameDefFieldPrefix(cr, false, false, sf);
defineClassReferenceNameDef(nd, cr, sf, mibh, prefix);
mb.createClassRefNameDefMapping(cm, nd, sf);
}
public void defineClassReferenceNameDef(NameDef nd,
ClassRef cr,
SpeedoField sf,
SpeedoClass currentClass,
GenClassMapping gcm,
MIBuilderHelper mibh,
JormMIMappingBuilder mb) throws SpeedoException, PException {
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "CompositeUserId: Filling the name def of a ClassRef from a class: "
+ "\n\tField: " + currentClass.getFQName() + "#" + sf.name
+ "\n\ttarget class=" + sf.getClassName());
}
String prefix = mibh.getNameDefFieldPrefix(cr, false, true, sf);
defineClassReferenceNameDef(nd, cr, sf, mibh, prefix);
mb.createClassRefNameDefMapping(gcm, nd, sf);
}
private void defineGenClassNameDef(NameDef nd,
GenClassRef gcr,
SpeedoField sf,
MIBuilderHelper mibh,
boolean isGCId) throws SpeedoException, PException {
String prefix = mibh.getNameDefFieldPrefix(gcr, isGCId, isGCId, sf);
Manager manager = mibh.getManager(gcr);
SpeedoClass clazzWithIdField = sf.moClass.getAncestor();
if (clazzWithIdField == null) {
clazzWithIdField = sf.moClass;
}
CompositeName cn = manager.getCompositeName(clazzWithIdField.identity.objectidClass);
NameRef nr = nd.createNameRef(cn);
List idFields = clazzWithIdField.getPKFields();
for (int i = 0; i < idFields.size(); i++) {
SpeedoField pkField = (SpeedoField) idFields.get(i);
// create a hidden field in the class corresponding to this
// composite Name field. The name is based on a prefix (GenClassRef name)
String fieldName = prefix + pkField.name;
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "\tField: name=" + fieldName);
}
if (isGCId) {
PType type = mibh.getPrimitivePType(Type.getType(pkField.type));
int size = PType.NOSIZE;
int scale = PType.NOSIZE;
if (pkField.columns != null && pkField.columns.length == 1) {
if (pkField.columns[0].length != -1) {
size = pkField.columns[0].length;
}
if (pkField.columns[0].scale != -1) {
scale = pkField.columns[0].scale;
}
}
mibh.createNameDefField(gcr, fieldName, type, size, scale);
}
//define the projection between composite name fields and field used
// in NameRef
nr.addProjection(pkField.name, fieldName);
}
}
public void defineGenClassIdentifierNameDef(NameDef nd,
GenClassRef gcr,
SpeedoField sf,
SpeedoClass currentClass,
GenClassMapping gcm,
MIBuilderHelper mibh,
JormMIMappingBuilder mb) throws SpeedoException, PException {
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "CompositeUserId: Filling the name def identifier of a GenClassRef: "
+ "\n\tField: " + currentClass.getFQName() + "#" + sf.name);
}
defineGenClassNameDef(nd, gcr, sf, mibh, true);
mb.createGenClassIdentifierNameDefMapping(gcm, nd, sf, mibh);
}
public void defineGenClassReferenceNameDef(NameDef nd,
GenClassRef gcr,
SpeedoField sf,
SpeedoClass currentClass,
ClassMapping cm,
MIBuilderHelper mibh, JormMIMappingBuilder mb)
throws SpeedoException, PException {
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "CompositeUserId: Filling the name def of a GenClassRef: "
+ "\n\tField: " + currentClass.getFQName() + "#" + sf.name);
}
defineGenClassNameDef(nd, gcr, sf, mibh, false);
}
public String getPNameHints(SpeedoClass sc, NameDef nd) {
return "this." + POVariableNames.REFSTATE_FIELD_NAME;
}
public Object[] getPNameHints2(SpeedoClass sc, NameDef nd) {
return new Object[]{PNH_REF_STATE};
}
public String getGCPNameHints(SpeedoClass sc, NameDef nd) {
return "speedoPO.getPName()";
}
/**
* Build a property value compsed such as this pattern
* userid,binder_class_name,pnc_class_name,class_name
* @param nd
* @param targetClass
* @param sourceMO
* @param key
* @param result
*/
public void getJormNamingConfig(NameDef nd,
SpeedoClass targetClass,
MetaObject sourceMO,
String key,
Properties result) throws SpeedoException {
StringBuffer sb = new StringBuffer();
sb.append(COMPOSITE_USER_ID);
sb.append(HINTS_SEP);
String binderClassName = NamingRules.binderName(
nd.getNameRef().getCompositeName().getFQName());
sb.append(binderClassName);
sb.append(HINTS_SEP);
String className = targetClass.getFQName();
SpeedoClass ancestor = targetClass.getAncestor();
if (sourceMO instanceof GenClassRef //For a Genclass ref or identifier
|| (ancestor == null //No inheritance
&& targetClass.jormclass.getSubClasses().isEmpty())) {
sb.append(binderClassName);
} else {
if (ancestor == null) {
ancestor = targetClass;
}
try {
logger.log(BasicLevel.DEBUG, "Class " + targetClass.jormclass.getFQName() + "\n"
+ " NamedDef " + nd.toString() + "\n"
+ " Filter " + ExpressionPrinter.e2str(targetClass.jormclass.getInheritanceFilter(nd)) );
if (targetClass.jormclass.getSubClasses().isEmpty()) {
sb.append(binderClassName);
} else if (targetClass.jormclass.detectFilterElementNotInPK(targetClass.jormclass.getInheritanceFilter(nd), nd)) {
//if has child(ren)
//and one of the fields of the filter is not in the primary key,
//use a PolymorphicPNC
logger.log(BasicLevel.DEBUG, "Composite PName : assign a polymorphic PNC for the class" + targetClass.jormclass.getFQName() + ".");
sb.append(POLYMORPHIC_PNC);
} else {
//use a kfpnc
logger.log(BasicLevel.DEBUG, "Composite PName : assign a KFPNC for the class" + targetClass.jormclass.getFQName() + ".");
sb.append(NamingRules.kfpncName(ancestor.getFQName()));
}
} catch (Exception e) {
logger.log(BasicLevel.ERROR, "Error while retrieving the inheritance filter of the namedef:" + nd.toString());
throw new SpeedoException("Error while retrieving the inheritance filter of the namedef:" + nd.toString(), e);
}
className = ancestor.getFQName();
}
sb.append(HINTS_SEP);
sb.append(className);
result.setProperty(key, sb.toString());
}
}