/**
* 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.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.objectweb.jorm.api.PClassMapping;
import org.objectweb.jorm.api.PException;
import org.objectweb.jorm.api.PMapper;
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.metainfo.api.PrimitiveElement;
import org.objectweb.jorm.metainfo.api.ScalarField;
import org.objectweb.jorm.naming.api.PBinder;
import org.objectweb.jorm.naming.api.PName;
import org.objectweb.jorm.naming.api.PNameCoder;
import org.objectweb.jorm.naming.api.PNamingContext;
import org.objectweb.jorm.type.api.PType;
import org.objectweb.medor.expression.api.Expression;
import org.objectweb.medor.expression.api.ExpressionException;
import org.objectweb.medor.expression.lib.BasicOperand;
import org.objectweb.medor.expression.lib.BasicParameterOperand;
import org.objectweb.medor.expression.lib.DivideBy;
import org.objectweb.medor.expression.lib.Round;
import org.objectweb.perseus.cache.api.CacheManager;
import org.objectweb.speedo.api.SpeedoException;
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.SpeedoColumn;
import org.objectweb.speedo.metadata.SpeedoField;
import org.objectweb.speedo.naming.api.MIBuilderHelper;
import org.objectweb.speedo.naming.api.NamingManager;
import org.objectweb.speedo.pm.api.POManagerFactoryItf;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
/**
* Defines a common implementation of NamingManager for identifier based on
* a single long or java.lang.Long field.
*
* @author S.Chassande-Barrioz
*/
public abstract class CommonLongIdNamingManager
extends NamingManagerHelper {
/**
* @return the name of the composite name (JORM object) corresponding the
* identifier.
*/
protected abstract String getLongIdName();
/**
* @return the default name of the identifier field.
*/
protected abstract String getLongIdLid();
/**
* @return the default jorm type of the identifier field.
*/
protected abstract PType getFieldType();
/**
* @return the default java type of the identifier field.
*/
protected abstract java.lang.Class getJavaFieldType();
/**
* @return the operand (Medor object) used for computing the class identifier
*/
protected abstract BasicOperand getBasicOperand();
/**
* Checks if the field type is the one expected
* @param type is the field type as a string.
* @return true if this the right type.
*/
protected abstract boolean checkFieldType(String type);
/**
* @return the mapper used
*/
public abstract PMapper getMapper();
/**
* @return a new PBinder for managing GenClass identifier.
*/
public abstract PBinder newGenClassPBinder() throws PException;
/**
* @param className is the class name of the persistent class
* @param conn is the connection to the underlying persistent support.
* @return a new PBinder for managing class identifier.
*/
public abstract PBinder newClassPBinder(String className, Object conn) throws PException;
/**
* @return a new PBinder for managing class reference.
*/
public abstract PNamingContext newClassPNamingContext() throws PException;
/**
* @return the class of the PName used by this naming.
*/
public abstract java.lang.Class getPNameClass();
/**
* @return the class of the PBinder used by this naming.
*/
public abstract java.lang.Class getPBinderClass();
/**
* @return the class of the IdManager used by this naming.
*/
public abstract java.lang.Class getLongIdManagerClass();
/**
* @return the class of the PNamingContext used by this naming.
*/
public abstract java.lang.Class getPNamingContextClass();
/**
* Decodes a string value into a PName.
* @param pnc is the PNameCoder able to decode the string
* @param idStr is the string representation of an identifier
* @return a PName
*/
protected abstract PName decodeLong(PNameCoder pnc, String idStr) throws PException;
/**
* Encodes a PName into a value.
* @param pn is the PName to encode
* @return the encoded PName
*/
public abstract Object encode(PName pn) throws PException;
/**
* @return Returns the bINDER_FOR_GENCLASS.
*/
protected abstract String getBinderForGenClass();
/**
* @return Returns the bINDER_FOR_CLASS.
*/
protected abstract String getBinderForClass();
/**
* @return Returns the hIDDEN_LID_FIELD_NAME.
*/
protected abstract String getHiddenLidFieldName();
public void init() throws PException {
if (getMapper() != null) {
return;
}
if (mapper == null) {
throw new PException("No mapper assigned");
}
setPMapper(mapper);
mapper = null;
}
// IMPLEMENTATION OF THE METHOD FROM THE NamingManager INTERFACE //
//---------------------------------------------------------------//
public void setPmf(POManagerFactoryItf pmf) {
}
public boolean supportPNamingcontext() {
return true;
}
public abstract boolean canManage(SpeedoClass sc);
public boolean canProvidePBinder(Object hints, ClassLoader classLoader) {
return getBinderClassNameFromHints(hints, getName()) != null;
}
public boolean canProvidePNamingContext(Object hints, ClassLoader classLoader) {
return getPNCClassNameFromHints(hints, getName()) != null;
}
public PName decode(PNameCoder pnc,
Object oid,
java.lang.Class clazz,
JormFactory jf) throws PException {
if (oid instanceof String) {
String stroid = (String) oid;
int idx = stroid.indexOf(SEP);
if (pnc != null) {
if (getPBinderClass().isInstance(pnc)
|| getLongIdManagerClass().isInstance(pnc)) {
String idStr = stroid;
if (idx != -1) {
//The oid contains the class name
idStr = stroid.substring(idx + SEP.length());
}
return decodeLong(pnc, idStr);
} else {
//The pnc is not a well known pnc, then the oid cannot be managed
return null;
}
} else {
//No pnc specified
if (idx != -1) {
//The oid contains the class name
String fqcn = stroid.substring(0, idx);
ClassLoader cl = getClass().getClassLoader();
if (cl == null) {
cl = ClassLoader.getSystemClassLoader();
}
try {
pnc = jf.getPNamingContext(fqcn, cl);
} catch (Exception e) {
return null;
}
if (getPBinderClass().isInstance(pnc)
|| getPNamingContextClass().isInstance(pnc)) {
return decodeLong(pnc,
stroid.substring(idx + SEP.length()));
} else {
return null;
}
} else {
//The oid cannot be managed
return null;
}
}
}
return null;
}
public void setLogger(Logger logger) {
this.logger = logger;
}
public String getPNameHints(SpeedoClass sc, NameDef nd) {
return "null";
}
public Object[] getPNameHints2(SpeedoClass sc, NameDef nd) {
return new Object[]{PNH_NULL_VALUE};
}
public NamingManager.NamingField[] getNamingfields(SpeedoClass sc) {
SpeedoField sf;
try {
sf = sc.getUniquePKField();
} catch (SpeedoException e) {
sf = null;
}
if (sf != null) {
return new NamingManager.NamingField[]{
new NamingManager.NamingField(
sf.name, getJavaFieldType(), getLongIdLid(), getFieldType())};
} else {
return null;
}
}
public void defineClassIdentifierNameDef(NameDef nd,
Class jc,
SpeedoClass sc,
ClassMapping cm,
MIBuilderHelper mibh,
JormMIMappingBuilder mb,
Collection createdMOs)
throws SpeedoException, PException {
SpeedoField pkField = fetchUniqueLongPKField(sc);
PrimitiveElement lid;
String lidname;
if (pkField == null) {
if (sc.identity.columns == null) {
logger.log(BasicLevel.WARN,
"No mapping information for the identifier of the class '"
+ sc.getFQName() +"'.");
lidname = getHiddenLidFieldName();
} else {
lidname = sc.identity.columns[0].column.name;
}
// create an hidden identifier field
lid = mibh.createNameDefField(sc.jormclass, lidname, getFieldType());
} else {
//The identifier is mapped over a persistent field
lidname = pkField.name;
lid = mibh.getPrimitiveField(sc.jormclass, lidname);
if (lid == null) {
throw new SpeedoException(
mibh.getErrorMessage(sc, sc.jormclass, null)
+ " Impossible to get the field '" + lidname + "'");
}
}
Manager manager = mibh.getManager(jc);
CompositeName cn = getLongIdCompositeName(manager);
NameRef nr = nd.createNameRef(cn);
nr.addProjection(getLongIdLid(), lid.getName());
if (sc.jormclass.getSuperClasses().isEmpty()) {
// define the inheritance filter permiting to determine the class
// since an identifier (some bits are reserved for class
// identification)
Expression expr = new Round(new DivideBy(
new BasicParameterOperand(getFieldType(), getLongIdLid()),
getBasicOperand()));
try {
expr.compileExpression();
} catch (ExpressionException e) {
throw new SpeedoException("Impossible to compile the filter of longid naming", e);
}
sc.jormclass.setInheritanceFilter(nd, expr);
// assign a default value to each sub classes. The real value
// is computed at runtime from the database content.
assignDummyKeyToFamilly(sc.jormclass, nd);
}
if (pkField == null) {
mb.createClassIdentifierNameDefMapping(cm, nd, sc, mibh);
}
}
public boolean needInheritanceDiscriminator(SpeedoClass sc)
throws SpeedoException {
return false;
}
public void defineClassReferenceNameDef(NameDef nd,
ClassRef cr,
SpeedoField sf,
SpeedoClass currentClass,
ClassMapping cm,
MIBuilderHelper mibh,
JormMIMappingBuilder mb) throws SpeedoException, PException {
String lidname = mibh.getNameDefFieldPrefix(cr, false, false, sf)
+ getHiddenLidFieldName();
SpeedoClass referencedClass = sf.moClass.getSpeedoClassFromContext(
cr.getMOClass().getFQName());
//get the composite name
Manager manager = mibh.getManager(cr);
CompositeName cn = getLongIdCompositeName(manager);
NameRef nr = nd.createNameRef(cn);
//create the hidden field
mibh.createNameDefField(cr.getParent(), lidname, getFieldType());
//define translation betwen the composite name field and the hidden field
nr.addProjection(getLongIdLid(), lidname);
//map the created hidden field in the ClassMapping
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 {
String lidname = mibh.getNameDefFieldPrefix(cr, false, true, sf)
+ getHiddenLidFieldName();
SpeedoClass referencedClass = sf.moClass.getSpeedoClassFromContext(
cr.getMOClass().getFQName());
//get the composite name
Manager manager = mibh.getManager(cr);
CompositeName cn = getLongIdCompositeName(manager);
NameRef nr = nd.createNameRef(cn);
//create the hidden field
mibh.createNameDefField(cr.getParent(), lidname, getFieldType());
//define translation betwen the composite name field and the hidden field
nr.addProjection(getLongIdLid(), lidname);
//map the created hidden field in the GenClassMapping
mb.createClassRefNameDefMapping(gcm, nd, sf);
}
public void defineGenClassIdentifierNameDef(NameDef nd,
GenClassRef gcr,
SpeedoField sf,
SpeedoClass currentClass,
GenClassMapping gcm,
MIBuilderHelper mibh,
JormMIMappingBuilder mb) throws SpeedoException, PException {
String lidname = mibh.getNameDefFieldPrefix(gcr, true, true, sf)
+ getHiddenLidFieldName();
mibh.createNameDefField(gcr, lidname, getFieldType());
//Gen class identifier is based on basid
nd.setFieldName(lidname);
//map the created hidden field in the GenClassMapping
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 {
SpeedoField pkfield = sf.moClass.getUniquePKField();
String fn;
if (pkfield == null) {
fn = getHiddenLidFieldName();
} else {
fn = pkfield.name;
}
//Gen class reference is based on basid
nd.setFieldName(fn);
}
private CompositeName getLongIdCompositeName(Manager manager) {
CompositeName cn = manager.getCompositeName(getLongIdName());
if (cn == null) {
cn = manager.createCompositeName(getLongIdName());
ScalarField cnf = cn.createCompositeNameField(
getLongIdLid(), getFieldType(), PType.NOSIZE, PType.NOSIZE);
cnf.setIsAutoCalculated(true);
}
return cn;
}
private void assignDummyKeyToFamilly(org.objectweb.jorm.metainfo.api.Class clazz, NameDef nd) {
clazz.setInheritanceNamingKey(nd, "0");
for(Iterator it = clazz.getSubClasses().iterator(); it.hasNext();) {
assignDummyKeyToFamilly(((org.objectweb.jorm.metainfo.api.Class) it.next()), nd);
}
}
private SpeedoField fetchUniqueLongPKField(SpeedoClass tsc) throws SpeedoException {
Iterator it = tsc.fields.values().iterator();
SpeedoField sf = null;
ArrayList al = null;
while (it.hasNext()) {
SpeedoField _sf = (SpeedoField) it.next();
if (_sf.primaryKey) {
if (sf != null) {
if (al == null) {
al = new ArrayList();
}
al.add(_sf.name);
} else {
sf = _sf;
}
}
}
if (al != null) {
al.add(sf);
throw new SpeedoException(
"Impossible to use an auto incremented identifier if " +
"several fields have been marked as primary-key " + al
+ " in the class '" + tsc.getFQName() + "'.");
}
if (sf != null) {
String t = sf.type();
if (!checkFieldType(t)) {
throw new SpeedoException(
"Impossible to use an auto incremented identifier: " +
"the field type of '" + sf.name + "' is '" + sf.type()
+ "' and '" + getFieldType().getJavaName()
+ "' is expected (class '"
+ tsc.getFQName() + "'.");
}
}
return sf;
}
public void getJormNamingConfig(NameDef nd,
SpeedoClass targetClass,
MetaObject sourceMO,
String key,
Properties result) {
StringBuffer sb = new StringBuffer();
sb.append(getName());
sb.append(NamingManagerHelper.HINTS_SEP);
boolean isGCR = sourceMO instanceof GenClassRef;
String binderInfo = (isGCR ? getBinderForGenClass() : getBinderForClass());
sb.append(binderInfo);
SpeedoClass ancestor = targetClass.getAncestor();
String ancestorClassName;
String pncClassName;
if (!isGCR //For a Genclass ref or genclass identifier
&& (ancestor != null // has supper class
|| !targetClass.jormclass.getSubClasses().isEmpty()) // has sub class
) {
if (ancestor == null) {
ancestor = targetClass;
}
ancestorClassName = ancestor.getFQName();
pncClassName = NamingRules.kfpncName(ancestorClassName);
} else {
ancestorClassName = targetClass.getFQName();
pncClassName = binderInfo;
}
//specify the pnc className
sb.append(NamingManagerHelper.HINTS_SEP);
sb.append(pncClassName);
//specify the className of the ancestor
sb.append(NamingManagerHelper.HINTS_SEP);
sb.append(ancestorClassName);
result.setProperty(key, sb.toString());
}
public PBinder getPBinder(String className,
String hints,
ClassLoader classLoader,
byte mappingStructureRule,
Map cn2binder,
Map cn2pnc) throws PException {
init();
String binderInfo = getBinderClassNameFromHints(hints, getName());
return getPBinder(className, binderInfo);
}
public PNamingContext getPNamingContext(String className,
String hints,
ClassLoader classLoader,
byte mappingStructureRule,
Map cn2binder,
Map cn2pnc,
Manager miManager,
PClassMapping pcm) throws PException {
init();
String[] tokens = NamingManagerHelper.getTokens(hints);
PNamingContext pnc = null;
if (tokens[NamingManagerHelper.BINDER_IDX].equals(tokens[NamingManagerHelper.PNC_IDX])) {
//The binder must be used as PNamingContext
pnc = (PNamingContext) cn2binder.get(className);
if (pnc == null) {
//instanciate the binder/PNC
pnc = (PNamingContext) getPBinder(
className, tokens[NamingManagerHelper.BINDER_IDX]);
//register the binder/PNC as binder
cn2binder.put(className, pnc);
}
return pnc;
} else {
boolean register = false;
if (!tokens[NamingManagerHelper.PCLASS_IDX].equals(className)) {
//The PNC of THE ancestor must be used
pnc = (PNamingContext) cn2pnc.get(tokens[NamingManagerHelper.PCLASS_IDX]);
if (pnc == null) {
register = true;
}
}
if (pnc == null) {
//instanciate the PNC
pnc = newClassPNamingContext();
if (register) {
cn2pnc.put(tokens[NamingManagerHelper.PCLASS_IDX], pnc);
}
}
}
return pnc;
}
protected PBinder getPBinder(String className, String binderInfo) throws PException {
if (binderInfo.startsWith(getBinderForGenClass())) {
return newGenClassPBinder();
} else if (binderInfo.startsWith(getBinderForClass())) {
return newClassPBinder(className, null);
} else {
throw new PException("Bad binder info: " + binderInfo);
}
}
public void setCache(CacheManager cache) {
}
public SpeedoColumn[] getDefaultColumn(SpeedoClass sc) {
return new SpeedoColumn[] {new SpeedoColumn(getHiddenLidFieldName())};
}
}