001: /**
002: * Copyright (C) 2001-2004 France Telecom R&D
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package org.objectweb.speedo.naming.lib;
018:
019: import org.objectweb.jorm.api.PException;
020: import org.objectweb.jorm.facility.naming.rdbsequence.RdbSequenceBinder;
021: import org.objectweb.jorm.facility.naming.rdbsequence.RdbSequencePName;
022: import org.objectweb.jorm.metainfo.api.Class;
023: import org.objectweb.jorm.metainfo.api.ClassMapping;
024: import org.objectweb.jorm.metainfo.api.ClassRef;
025: import org.objectweb.jorm.metainfo.api.GenClassMapping;
026: import org.objectweb.jorm.metainfo.api.GenClassRef;
027: import org.objectweb.jorm.metainfo.api.MetaObject;
028: import org.objectweb.jorm.metainfo.api.NameDef;
029: import org.objectweb.jorm.naming.api.PBinder;
030: import org.objectweb.jorm.naming.api.PName;
031: import org.objectweb.jorm.naming.api.PNameCoder;
032: import org.objectweb.jorm.type.api.PType;
033: import org.objectweb.jorm.type.api.PTypeSpace;
034: import org.objectweb.speedo.api.SpeedoException;
035: import org.objectweb.speedo.generation.jorm.JormMIMappingBuilder;
036: import org.objectweb.speedo.generation.lib.NamingRules;
037: import org.objectweb.speedo.mapper.api.JormFactory;
038: import org.objectweb.speedo.metadata.SpeedoClass;
039: import org.objectweb.speedo.metadata.SpeedoColumn;
040: import org.objectweb.speedo.metadata.SpeedoField;
041: import org.objectweb.speedo.metadata.SpeedoIdentity;
042: import org.objectweb.speedo.naming.api.MIBuilderHelper;
043: import org.objectweb.speedo.naming.api.NamingManager;
044: import org.objectweb.speedo.sequence.api.SpeedoSequenceItf;
045: import org.objectweb.speedo.sequence.lib.SpeedoSequenceBinder;
046: import org.objectweb.speedo.tools.StringReplace;
047: import org.objectweb.util.monolog.api.BasicLevel;
048:
049: import java.util.Collection;
050: import java.util.Iterator;
051: import java.util.Map;
052: import java.util.Properties;
053:
054: /**
055: * This is naming manager manages identifier based sequence in database (SQL
056: * sequence or simple counter managed by Speedo).
057: * This identifier can be mapped over a persistent field (type must be long or
058: * java.lang.Long). This identifier format does not support polymorphism directly.
059: * You have to specify discriminator(s). But in all case, this naming is not an
060: * efficient choice when there is polymorphism (ex inheritance). Indeed SQL request
061: * to find a corresponding object is more complex. The null reference depends on
062: * the field type: -1 for long field and NULL or java.lang.Long field type.
063: *
064: * @see org.objectweb.speedo.naming.api.NamingManager
065: * @see org.objectweb.speedo.sequence.lib.SpeedoSequence
066: * @see org.objectweb.speedo.sequence.lib.SpeedoSequenceBinder
067: * @see org.objectweb.jorm.facility.naming.rdbsequence.RdbSequenceBinder
068: * @author S.Chassande-Barrioz
069: */
070: public class RdbSequenceNamingManager extends NamingManagerHelper
071: implements NamingManager {
072: private final static String ID_FIELD = "id_";
073:
074: private final static String SEQUENCE_ID = "sid";
075: private final static String SPEEDO_BINDER_CLASS_NAME = SpeedoSequenceBinder.class
076: .getName();
077: public final static int SEQ_NAME_IDX = 4;
078: public final static int SEQ_INCREMENT_IDX = 5;
079: public final static int SEQ_STARTID_IDX = 6;
080: public final static int SEQ_CACHE_IDX = 7;
081: public final static int SEQ_ALLOCATOR_IDX = 8;
082:
083: public boolean canManage(SpeedoClass sc) {
084: if (sc.identity.strategy == SpeedoIdentity.DATASTORE_SEQUENCE) {
085: return true;
086: }
087: return false;
088: }
089:
090: // IMPLEMENTATION OF THE METHOD FROM THE NamingManager INTERFACE //
091: //---------------------------------------------------------------//
092:
093: public PName decode(PNameCoder pnc, Object oid,
094: java.lang.Class clazz, JormFactory jf) throws PException {
095: if (oid instanceof String) {
096: String stroid = (String) oid;
097: if (pnc != null) {
098: if (pnc instanceof RdbSequenceBinder) {
099: int idx = stroid.indexOf(SEP);
100: if (idx != -1) {
101: //The oid contains the class name
102: return pnc.decodeString(stroid.substring(idx
103: + SEP.length()));
104: } else {
105: //The oid must decoded directly
106: return pnc.decodeString(stroid);
107: }
108:
109: } else {
110: //The pnc is not a RdbSequenceBinder, then the oid cannot be managed
111: return null;
112: }
113: } else {
114: //No pnc specified
115: int idx = stroid.indexOf(SEP);
116: ClassLoader cl = null;
117: String fqcn;
118: if (idx != -1) {
119: //The oid contains the class name
120: fqcn = stroid.substring(0, idx);
121: } else if (clazz != null) {
122: fqcn = clazz.getName();
123: } else {
124: //The oid cannot be managed
125: return null;
126: }
127: cl = jf.getClass().getClassLoader();
128: if (cl == null) {
129: cl = ClassLoader.getSystemClassLoader();
130: }
131: try {
132: pnc = jf.getPBinder(fqcn, cl);
133: } catch (Exception e) {
134: return null;
135: }
136: return (pnc instanceof RdbSequenceBinder ? pnc
137: .decodeString(stroid.substring(idx
138: + SEP.length())) : null);
139: }
140: } else if (pnc instanceof RdbSequenceBinder) {
141: if (oid instanceof Integer) {
142: pnc.decodeLong(((Integer) oid).intValue());
143: } else if (oid instanceof Long) {
144: pnc.decodeLong(((Long) oid).longValue());
145: }
146: }
147: return null;
148: }
149:
150: public Object encode(PName pn) throws PException {
151: if (pn instanceof RdbSequencePName) {
152: return pn.getPNameManager().getPType().getJormName() + SEP
153: + pn.encodeString();
154: }
155: return null;
156: }
157:
158: protected String getName() {
159: return SEQUENCE_ID;
160: }
161:
162: public PBinder getPBinder(String className, String hints,
163: ClassLoader cl, byte mappingStructureRule, Map cn2binder,
164: Map cn2pnc) throws PException {
165: SpeedoSequenceBinder binder = (SpeedoSequenceBinder) super
166: .getPBinder(className, hints, cl, mappingStructureRule,
167: cn2binder, cn2pnc);
168: String[] tokens = getTokens(hints);
169: if (tokens[SEQ_NAME_IDX].length() > 0) {
170: if (logger != null && logger.isLoggable(BasicLevel.DEBUG)) {
171: logger.log(BasicLevel.DEBUG, "Sequence name: "
172: + tokens[SEQ_NAME_IDX]);
173: }
174: String sequencePrefix = (className.lastIndexOf('.') != -1) ? className
175: .substring(0, className.lastIndexOf('.') + 1)
176: : "";
177: sequencePrefix = StringReplace.replaceString("/", "",
178: sequencePrefix);
179: SpeedoSequenceItf sequence = (SpeedoSequenceItf) pmf
180: .getSequenceManager().getSequence(
181: sequencePrefix + tokens[SEQ_NAME_IDX]);
182: if (sequence == null) {
183: throw new PException("Wrong sequence name: "
184: + sequencePrefix + tokens[SEQ_NAME_IDX]);
185: }
186: sequence.getLongGen();
187: binder.setSequence(sequence);
188: }
189: return binder;
190: }
191:
192: public void getJormNamingConfig(NameDef nd,
193: SpeedoClass targetClass, MetaObject sourceMO, String key,
194: Properties result) throws SpeedoException {
195: StringBuffer sb = new StringBuffer();
196: sb.append(SEQUENCE_ID);
197:
198: sb.append(HINTS_SEP);
199: sb.append(SPEEDO_BINDER_CLASS_NAME);
200:
201: SpeedoClass ancestor = targetClass.getAncestor();
202: String ancestorClassName;
203: String pncClassName;
204: if (!(sourceMO instanceof GenClassRef) //For a Genclass ref or identifier
205: && (ancestor != null // has supper class
206: || !targetClass.jormclass.getSubClasses().isEmpty()) // has sub class
207: ) {
208: if (ancestor == null) {
209: ancestor = targetClass;
210: }
211: ancestorClassName = ancestor.getFQName();
212: try {
213: if (targetClass.jormclass.getSubClasses().isEmpty()) {
214: pncClassName = SPEEDO_BINDER_CLASS_NAME;
215: } else if (targetClass.jormclass
216: .detectFilterElementNotInPK(
217: targetClass.jormclass
218: .getInheritanceFilter(nd), nd)) {
219: //if has child(ren)
220: //and one of the fields of the filter is not in the primary key,
221: //use a PolymorphicPNC
222: pncClassName = POLYMORPHIC_PNC;
223: } else {
224: //use a kfpnc
225: pncClassName = NamingRules.kfpncName(ancestor
226: .getFQName());
227: }
228: } catch (Exception e) {
229: logger.log(BasicLevel.ERROR,
230: "Error while retrieving the inheritance filter of the namedef:"
231: + nd.toString());
232: throw new SpeedoException(
233: "Error while retrieving the inheritance filter of the namedef:"
234: + nd.toString(), e);
235: }
236: } else {
237: ancestorClassName = targetClass.getFQName();
238: pncClassName = SPEEDO_BINDER_CLASS_NAME;
239: }
240: //specify the pnc className
241: sb.append(HINTS_SEP);
242: sb.append(pncClassName);
243:
244: //specify the className of the ancestor
245: sb.append(HINTS_SEP);
246: sb.append(ancestorClassName);
247:
248: //sequence name
249: sb.append(HINTS_SEP);
250: sb.append(targetClass.identity.sequenceName);
251: result.setProperty(key, sb.toString());
252: }
253:
254: public String getPNameHints(SpeedoClass sc, NameDef nd) {
255: return "null";
256: }
257:
258: public Object[] getPNameHints2(SpeedoClass sc, NameDef nd) {
259: return new Object[] { PNH_NULL_VALUE };
260: }
261:
262: public String getGCPNameHints(SpeedoClass sc, NameDef nd) {
263: return "speedoPO.getPName()";
264: }
265:
266: public void defineClassIdentifierNameDef(NameDef nd, Class jc,
267: SpeedoClass sc, ClassMapping cm, MIBuilderHelper mibh,
268: JormMIMappingBuilder mb, Collection createdMOs)
269: throws SpeedoException, PException {
270: SpeedoField pkField = sc.getUniquePKField();
271: String fieldName;
272: if (pkField == null) {
273: if (sc.identity.columns == null) {
274: logger.log(BasicLevel.WARN,
275: "No mapping information for the identifier of the class '"
276: + sc.getFQName() + "'.");
277: fieldName = ID_FIELD;
278: } else {
279: fieldName = sc.identity.columns[0].column.name;
280: }
281: mibh.createNameDefField(jc, fieldName, PTypeSpace.OBJLONG);
282: } else {
283: String type = pkField.type();
284: if (!"java.lang.Long".equals(type) && !"Long".equals(type)
285: && !"long".equals(type)) {
286: throw new SpeedoException(
287: "Impossible to use an auto incremented identifier: "
288: + "the field type of '"
289: + pkField.name
290: + "' is '"
291: + pkField.type()
292: + "' and 'java.lang.Long' or long is expected (class '"
293: + sc.getFQName() + "'.");
294: }
295: fieldName = pkField.name;
296: }
297: nd.setFieldName(fieldName);
298: if (pkField == null) {
299: mb.createClassIdentifierNameDefMapping(cm, nd, sc, mibh);
300: }
301: }
302:
303: private void defineClassReferenceNameDef(NameDef nd, ClassRef cr,
304: SpeedoField sf, MIBuilderHelper mibh, String prefix)
305: throws SpeedoException, PException {
306: SpeedoClass referencedClass = sf.moClass
307: .getSpeedoClassFromContext(cr.getMOClass().getFQName());
308: SpeedoField pkField = (SpeedoField) referencedClass
309: .getUniquePKField();
310: String fieldName = null;
311: PType fieldType;
312: if (pkField == null) {
313: fieldName = ID_FIELD;
314: fieldType = PTypeSpace.OBJLONG;
315: } else {
316: String type = pkField.type();
317: if ("java.lang.Long".equals(type) || "Long".equals(type)) {
318: fieldType = PTypeSpace.OBJLONG;
319: } else if ("long".equals(type)) {
320: fieldType = PTypeSpace.LONG;
321: } else {
322: throw new SpeedoException(
323: "Impossible to use an auto incremented identifier: "
324: + "the field type of '"
325: + pkField.name
326: + "' is '"
327: + pkField.type()
328: + "' and 'java.lang.Long' or long is expected (class '"
329: + pkField.moClass.getFQName() + "'.");
330: }
331: fieldName = pkField.name;
332: }
333: fieldName = prefix + fieldName;
334: mibh.createNameDefField(cr.getParent(), fieldName, fieldType);
335: nd.setFieldName(fieldName);
336:
337: }
338:
339: public void defineClassReferenceNameDef(NameDef nd, ClassRef cr,
340: SpeedoField sf, SpeedoClass currentClass, ClassMapping cm,
341: MIBuilderHelper mibh, JormMIMappingBuilder mb)
342: throws SpeedoException, PException {
343: String prefix = mibh
344: .getNameDefFieldPrefix(cr, false, false, sf);
345: defineClassReferenceNameDef(nd, cr, sf, mibh, prefix);
346: mb.createClassRefNameDefMapping(cm, nd, sf);
347: }
348:
349: public void defineClassReferenceNameDef(NameDef nd, ClassRef cr,
350: SpeedoField sf, SpeedoClass currentClass,
351: GenClassMapping gcm, MIBuilderHelper mibh,
352: JormMIMappingBuilder mb) throws SpeedoException, PException {
353: String prefix = mibh.getNameDefFieldPrefix(cr, false, true, sf);
354: defineClassReferenceNameDef(nd, cr, sf, mibh, prefix);
355: mb.createClassRefNameDefMapping(gcm, nd, sf);
356: }
357:
358: private void defineGenClassNameDef(NameDef nd, GenClassRef gcr,
359: SpeedoField sf, MIBuilderHelper mibh, boolean isGCId)
360: throws SpeedoException, PException {
361: //Find the unique pkField in order to get its name and its type
362: String fieldName;
363: PType fieldType;
364: SpeedoField pkField = sf.moClass.getUniquePKField();
365: if (isGCId) {
366: //create hidden field in the GenClassRef for its identifier
367: // the create field has a name built from the GCR name itself
368: if (pkField == null) {
369: //there is no pk field defined ==> new one
370: fieldName = ID_FIELD;
371: fieldType = PTypeSpace.OBJLONG;
372: } else {
373: fieldName = pkField.name;
374: //Check the PK field type (Long or long only supported)
375: String type = pkField.type();
376: if ("java.lang.Long".equals(type)
377: || "Long".equals(type)) {
378: fieldType = PTypeSpace.OBJLONG;
379: } else if ("long".equals(type)) {
380: fieldType = PTypeSpace.LONG;
381: } else {
382: throw new SpeedoException(
383: "Impossible to use an auto incremented identifier: "
384: + "the field type of '"
385: + pkField.name
386: + "' is '"
387: + pkField.type()
388: + "' and 'java.lang.Long' or long is expected (class '"
389: + pkField.moClass.getFQName()
390: + "'.");
391: }
392: }
393: String prefix = mibh.getNameDefFieldPrefix(gcr, isGCId,
394: isGCId, sf);
395: fieldName = prefix + fieldName;
396: mibh.createNameDefField(gcr, fieldName, fieldType);
397: } else {
398: // In case of reference to a generic class, the pkfield of the
399: // current class is used
400: if (pkField == null) {
401: //there is no pk field defined ==> new one
402: fieldName = ID_FIELD;
403: } else {
404: fieldName = pkField.name;
405: }
406: }
407: nd.setFieldName(fieldName);
408: }
409:
410: public void defineGenClassIdentifierNameDef(NameDef nd,
411: GenClassRef gcr, SpeedoField sf, SpeedoClass currentClass,
412: GenClassMapping gcm, MIBuilderHelper mibh,
413: JormMIMappingBuilder mb) throws SpeedoException, PException {
414: defineGenClassNameDef(nd, gcr, sf, mibh, true);
415: mb.createGenClassIdentifierNameDefMapping(gcm, nd, sf, mibh);
416: }
417:
418: public void defineGenClassReferenceNameDef(NameDef nd,
419: GenClassRef gcr, SpeedoField sf, SpeedoClass currentClass,
420: ClassMapping cm, MIBuilderHelper mibh,
421: JormMIMappingBuilder mb) throws SpeedoException, PException {
422: defineGenClassNameDef(nd, gcr, sf, mibh, false);
423: }
424:
425: public NamingField[] getNamingfields(SpeedoClass sc)
426: throws PException {
427: Iterator it = sc.fields.values().iterator();
428: while (it.hasNext()) {
429: SpeedoField sf = (SpeedoField) it.next();
430: if (sf.primaryKey) {
431: java.lang.Class fieldType;
432: PType fieldPType;
433: if ("java.lang.Long".equals(sf.type())
434: || "Long".equals(sf.type())) {
435: fieldType = Long.class;
436: fieldPType = PTypeSpace.OBJLONG;
437: } else if ("long".equals(sf.type())) {
438: fieldType = Long.TYPE;
439: fieldPType = PTypeSpace.LONG;
440: } else {
441: throw new PException(
442: "Impossible to use an auto incremented identifier: "
443: + "the field type of '"
444: + sf.name
445: + "' is '"
446: + sf.type()
447: + "' and 'java.lang.Long' or 'long' is expected (class '"
448: + sc.getFQName() + "'.");
449: }
450: return new NamingField[] { new NamingField(sf.name,
451: fieldType, "lid", fieldPType) };
452: }
453: }
454: return null;
455: }
456:
457: public SpeedoColumn[] getDefaultColumn(SpeedoClass sc) {
458: return new SpeedoColumn[] { new SpeedoColumn("lid") };
459: }
460:
461: }
|