/**
* 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 org.objectweb.jorm.api.PException;
import org.objectweb.jorm.facility.naming.rdbsequence.RdbSequenceBinder;
import org.objectweb.jorm.facility.naming.rdbsequence.RdbSequencePName;
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.GenClassMapping;
import org.objectweb.jorm.metainfo.api.GenClassRef;
import org.objectweb.jorm.metainfo.api.MetaObject;
import org.objectweb.jorm.metainfo.api.NameDef;
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.type.api.PType;
import org.objectweb.jorm.type.api.PTypeSpace;
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.metadata.SpeedoIdentity;
import org.objectweb.speedo.naming.api.MIBuilderHelper;
import org.objectweb.speedo.naming.api.NamingManager;
import org.objectweb.speedo.sequence.api.SpeedoSequenceItf;
import org.objectweb.speedo.sequence.lib.SpeedoSequenceBinder;
import org.objectweb.speedo.tools.StringReplace;
import org.objectweb.util.monolog.api.BasicLevel;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
* This is naming manager manages identifier based sequence in database (SQL
* sequence or simple counter managed by Speedo).
* This identifier can be mapped over a persistent field (type must be long or
* java.lang.Long). This identifier format does not support polymorphism directly.
* You have to specify discriminator(s). But in all case, this naming is not an
* efficient choice when there is polymorphism (ex inheritance). Indeed SQL request
* to find a corresponding object is more complex. The null reference depends on
* the field type: -1 for long field and NULL or java.lang.Long field type.
*
* @see org.objectweb.speedo.naming.api.NamingManager
* @see org.objectweb.speedo.sequence.lib.SpeedoSequence
* @see org.objectweb.speedo.sequence.lib.SpeedoSequenceBinder
* @see org.objectweb.jorm.facility.naming.rdbsequence.RdbSequenceBinder
* @author S.Chassande-Barrioz
*/
public class RdbSequenceNamingManager
extends NamingManagerHelper
implements NamingManager {
private final static String ID_FIELD ="id_";
private final static String SEQUENCE_ID ="sid";
private final static String SPEEDO_BINDER_CLASS_NAME
= SpeedoSequenceBinder.class.getName();
public final static int SEQ_NAME_IDX = 4;
public final static int SEQ_INCREMENT_IDX = 5;
public final static int SEQ_STARTID_IDX = 6;
public final static int SEQ_CACHE_IDX = 7;
public final static int SEQ_ALLOCATOR_IDX = 8;
public boolean canManage(SpeedoClass sc) {
if (sc.identity.strategy == SpeedoIdentity.DATASTORE_SEQUENCE) {
return true;
}
return false;
}
// IMPLEMENTATION OF THE METHOD FROM THE NamingManager INTERFACE //
//---------------------------------------------------------------//
public PName decode(PNameCoder pnc,
Object oid,
java.lang.Class clazz,
JormFactory jf) throws PException {
if (oid instanceof String) {
String stroid = (String) oid;
if (pnc != null) {
if (pnc instanceof RdbSequenceBinder) {
int idx = stroid.indexOf(SEP);
if (idx != -1) {
//The oid contains the class name
return pnc.decodeString(stroid.substring(idx + SEP.length()));
} else {
//The oid must decoded directly
return pnc.decodeString(stroid);
}
} else {
//The pnc is not a RdbSequenceBinder, then the oid cannot be managed
return null;
}
} else {
//No pnc specified
int idx = stroid.indexOf(SEP);
ClassLoader cl = null;
String fqcn;
if (idx != -1) {
//The oid contains the class name
fqcn = stroid.substring(0, idx);
} else if (clazz != null) {
fqcn = clazz.getName();
} else {
//The oid cannot be managed
return null;
}
cl = jf.getClass().getClassLoader();
if (cl == null) {
cl = ClassLoader.getSystemClassLoader();
}
try {
pnc = jf.getPBinder(fqcn, cl);
} catch (Exception e) {
return null;
}
return (pnc instanceof RdbSequenceBinder
? pnc.decodeString(stroid.substring(idx + SEP.length()))
: null);
}
} else if (pnc instanceof RdbSequenceBinder){
if (oid instanceof Integer) {
pnc.decodeLong(((Integer) oid).intValue());
} else if (oid instanceof Long) {
pnc.decodeLong(((Long) oid).longValue());
}
}
return null;
}
public Object encode(PName pn) throws PException {
if (pn instanceof RdbSequencePName) {
return pn.getPNameManager().getPType().getJormName()
+ SEP + pn.encodeString();
}
return null;
}
protected String getName() {
return SEQUENCE_ID;
}
public PBinder getPBinder(String className,
String hints,
ClassLoader cl,
byte mappingStructureRule,
Map cn2binder,
Map cn2pnc) throws PException {
SpeedoSequenceBinder binder = (SpeedoSequenceBinder) super.getPBinder(
className, hints, cl, mappingStructureRule, cn2binder, cn2pnc);
String[] tokens = getTokens(hints);
if (tokens[SEQ_NAME_IDX].length() > 0) {
if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "Sequence name: " + tokens[SEQ_NAME_IDX]);
}
String sequencePrefix = (className.lastIndexOf('.') != -1) ? className.substring(0, className.lastIndexOf('.')+1):"";
sequencePrefix = StringReplace.replaceString("/", "", sequencePrefix);
SpeedoSequenceItf sequence = (SpeedoSequenceItf) pmf.getSequenceManager()
.getSequence(sequencePrefix + tokens[SEQ_NAME_IDX]);
if (sequence == null) {
throw new PException("Wrong sequence name: " + sequencePrefix + tokens[SEQ_NAME_IDX]);
}
sequence.getLongGen();
binder.setSequence(sequence);
}
return binder;
}
public void getJormNamingConfig(NameDef nd,
SpeedoClass targetClass,
MetaObject sourceMO,
String key,
Properties result) throws SpeedoException{
StringBuffer sb = new StringBuffer();
sb.append(SEQUENCE_ID);
sb.append(HINTS_SEP);
sb.append(SPEEDO_BINDER_CLASS_NAME);
SpeedoClass ancestor = targetClass.getAncestor();
String ancestorClassName;
String pncClassName;
if (!(sourceMO instanceof GenClassRef) //For a Genclass ref or identifier
&& (ancestor != null // has supper class
|| !targetClass.jormclass.getSubClasses().isEmpty()) // has sub class
) {
if (ancestor == null) {
ancestor = targetClass;
}
ancestorClassName = ancestor.getFQName();
try {
if (targetClass.jormclass.getSubClasses().isEmpty()) {
pncClassName = SPEEDO_BINDER_CLASS_NAME;
} 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
pncClassName = POLYMORPHIC_PNC;
} else {
//use a kfpnc
pncClassName = 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);
}
} else {
ancestorClassName = targetClass.getFQName();
pncClassName = SPEEDO_BINDER_CLASS_NAME;
}
//specify the pnc className
sb.append(HINTS_SEP);
sb.append(pncClassName);
//specify the className of the ancestor
sb.append(HINTS_SEP);
sb.append(ancestorClassName);
//sequence name
sb.append(HINTS_SEP);
sb.append(targetClass.identity.sequenceName);
result.setProperty(key, sb.toString());
}
public String getPNameHints(SpeedoClass sc, NameDef nd) {
return "null";
}
public Object[] getPNameHints2(SpeedoClass sc, NameDef nd) {
return new Object[]{PNH_NULL_VALUE};
}
public String getGCPNameHints(SpeedoClass sc, NameDef nd) {
return "speedoPO.getPName()";
}
public void defineClassIdentifierNameDef(NameDef nd,
Class jc,
SpeedoClass sc,
ClassMapping cm,
MIBuilderHelper mibh,
JormMIMappingBuilder mb,
Collection createdMOs) throws SpeedoException, PException {
SpeedoField pkField = sc.getUniquePKField();
String fieldName;
if (pkField == null) {
if (sc.identity.columns == null) {
logger.log(BasicLevel.WARN,
"No mapping information for the identifier of the class '"
+ sc.getFQName() +"'.");
fieldName = ID_FIELD;
} else {
fieldName = sc.identity.columns[0].column.name;
}
mibh.createNameDefField(jc, fieldName, PTypeSpace.OBJLONG);
} else {
String type = pkField.type();
if (!"java.lang.Long".equals(type) && !"Long".equals(type)
&& !"long".equals(type)) {
throw new SpeedoException(
"Impossible to use an auto incremented identifier: " +
"the field type of '" + pkField.name + "' is '" + pkField.type()
+ "' and 'java.lang.Long' or long is expected (class '"
+ sc.getFQName() + "'.");
}
fieldName = pkField.name;
}
nd.setFieldName(fieldName);
if (pkField == null) {
mb.createClassIdentifierNameDefMapping(cm, nd, sc,mibh);
}
}
private void defineClassReferenceNameDef(NameDef nd,
ClassRef cr,
SpeedoField sf,
MIBuilderHelper mibh,
String prefix) throws SpeedoException, PException {
SpeedoClass referencedClass = sf.moClass.getSpeedoClassFromContext(
cr.getMOClass().getFQName());
SpeedoField pkField = (SpeedoField) referencedClass.getUniquePKField();
String fieldName = null;
PType fieldType;
if (pkField == null) {
fieldName = ID_FIELD;
fieldType = PTypeSpace.OBJLONG;
} else {
String type = pkField.type();
if ("java.lang.Long".equals(type) || "Long".equals(type)) {
fieldType = PTypeSpace.OBJLONG;
} else if ("long".equals(type)) {
fieldType = PTypeSpace.LONG;
} else {
throw new SpeedoException(
"Impossible to use an auto incremented identifier: " +
"the field type of '" + pkField.name + "' is '" + pkField.type()
+ "' and 'java.lang.Long' or long is expected (class '"
+ pkField.moClass.getFQName() + "'.");
}
fieldName = pkField.name;
}
fieldName = prefix + fieldName;
mibh.createNameDefField(cr.getParent(), fieldName, fieldType);
nd.setFieldName(fieldName);
}
public void defineClassReferenceNameDef(NameDef nd,
ClassRef cr,
SpeedoField sf,
SpeedoClass currentClass,
ClassMapping cm,
MIBuilderHelper mibh,
JormMIMappingBuilder mb) throws SpeedoException, PException {
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 {
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 {
//Find the unique pkField in order to get its name and its type
String fieldName;
PType fieldType;
SpeedoField pkField = sf.moClass.getUniquePKField();
if (isGCId) {
//create hidden field in the GenClassRef for its identifier
// the create field has a name built from the GCR name itself
if (pkField == null) {
//there is no pk field defined ==> new one
fieldName = ID_FIELD;
fieldType = PTypeSpace.OBJLONG;
} else {
fieldName = pkField.name;
//Check the PK field type (Long or long only supported)
String type = pkField.type();
if ("java.lang.Long".equals(type) || "Long".equals(type)) {
fieldType = PTypeSpace.OBJLONG;
} else if ("long".equals(type)) {
fieldType = PTypeSpace.LONG;
} else {
throw new SpeedoException(
"Impossible to use an auto incremented identifier: " +
"the field type of '" + pkField.name + "' is '" + pkField.type()
+ "' and 'java.lang.Long' or long is expected (class '"
+ pkField.moClass.getFQName() + "'.");
}
}
String prefix = mibh.getNameDefFieldPrefix(gcr, isGCId, isGCId, sf);
fieldName = prefix + fieldName;
mibh.createNameDefField(gcr, fieldName, fieldType);
} else {
// In case of reference to a generic class, the pkfield of the
// current class is used
if (pkField == null) {
//there is no pk field defined ==> new one
fieldName = ID_FIELD;
} else {
fieldName = pkField.name;
}
}
nd.setFieldName(fieldName);
}
public void defineGenClassIdentifierNameDef(NameDef nd,
GenClassRef gcr,
SpeedoField sf,
SpeedoClass currentClass,
GenClassMapping gcm,
MIBuilderHelper mibh,
JormMIMappingBuilder mb) throws SpeedoException, PException {
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 {
defineGenClassNameDef(nd, gcr, sf, mibh, false);
}
public NamingField[] getNamingfields(SpeedoClass sc) throws PException {
Iterator it = sc.fields.values().iterator();
while(it.hasNext()) {
SpeedoField sf = (SpeedoField) it.next();
if (sf.primaryKey) {
java.lang.Class fieldType;
PType fieldPType;
if ("java.lang.Long".equals(sf.type()) || "Long".equals(sf.type())) {
fieldType = Long.class;
fieldPType = PTypeSpace.OBJLONG;
} else if ("long".equals(sf.type())) {
fieldType = Long.TYPE;
fieldPType = PTypeSpace.LONG;
} else {
throw new PException(
"Impossible to use an auto incremented identifier: " +
"the field type of '" + sf.name + "' is '" + sf.type()
+ "' and 'java.lang.Long' or 'long' is expected (class '"
+ sc.getFQName() + "'.");
}
return new NamingField[] {
new NamingField(sf.name, fieldType, "lid", fieldPType)};
}
}
return null;
}
public SpeedoColumn[] getDefaultColumn(SpeedoClass sc) {
return new SpeedoColumn[] {new SpeedoColumn("lid")};
}
}