/**
* Speedo: an implementation of JDO compliant personality on top of JORM generic
* I/O sub-system.
* 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
*
*
*
* Contact: speedo@objectweb.org
*
* Authors: S.Chassande-Barrioz.
*
*/
package org.objectweb.speedo.metadata;
import org.objectweb.jorm.metainfo.api.Class;
import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.api.SpeedoRuntimeException;
import org.objectweb.speedo.mim.api.HomeItf;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Describes a persistence capable class.
*
* @author S.Chassande-Barrioz
*/
public class SpeedoClass extends SpeedoElement {
/**
* The class has been previously enhanced and no additional operation is
* required.
*/
public final static byte ALREADY_ENHANCED = 1;
/**
* The enhancement is required.
*/
public final static byte ENHANCEMENT_REQUIRED = 2;
/**
* The enhancement of the class has failed.
*/
public final static byte ENHANCEMENT_FAILED = 3;
/**
* The persistent class has no no-arg constructor.
*/
public final static byte NO_NO_ARG_CONSTRUCTOR = 1;
/**
* The persistent class has no-arg constructor but it is not public
*/
public final static byte NON_PUBLIC_NO_ARG_CONSTRUCTOR = 2;
/**
* The persistent class has a public no-arg constructor.
*/
public final static byte PUBLIC_NO_ARG_CONSTRUCTOR = 3;
/**
* Class name.
*/
public String name;
/**
* Class name for queries.
*/
public String nameForQuery;
/**
* Description of the package which contains this class.
*/
public SpeedoPackage moPackage;
/**
* Defines the identifier of the persistent class.
*/
public SpeedoIdentity identity;
/**
* Defines the field used for database optimitic locking policy.
*/
public SpeedoField versionField;
/**
* Defines the inheritance strategy if this class inherits from a persistent
* class.
*/
public SpeedoInheritance inheritance;
/**
* Description of the version.
*/
public SpeedoVersion version;
/**
* Attribute detachable.
*/
public boolean isDetachable;
/**
* Description of persistent capable fields of this class. The HashMap key
* is the field name.
*/
public Map fields;
/**
* Description of fetchgroups defined for this class. The HashMap key is the
* field name.
*/
public Map fetchGroups;
/**
* Indicates if the class is abstract.
*/
public boolean isAbstract;
/**
* Indicates if the class implements InstanceCallbacks
*/
public boolean isInstanceCallbacks;
/**
* Indicates if the class implements Serailizable
*/
public boolean isSerializable;
/**
* JORM meta object corresponding to this Speedo meta object.
*/
public Class jormclass;
/**
* Register some callback methods and their associated application method to
* which the callback is mapped.
* It contains an Integer key identifying a particular callback, which is associated
* with an ArrayList of methods to be called when this callback is fired.
* Such methods are specified through a metaobject specifying the callback:
* @see SpeedoCallback
* @see HomeItf#PRE_NEW
*/
public HashMap callBacks;
/**
* Number of fields of this class (including inherited fields).
*/
private int fieldsCount = -1;
/**
* Number of fields composing the primary key
*/
private int pkFieldsCount = -1;
/**
* Specify the status of the persistent class with regards to the
* enhancement process. The 3 only possible state are #ALREADY_ENHANCED,
* #ENHANCEMENT_REQUIRED or #ENHANCEMENT_FAILED.
*
* @see #ALREADY_ENHANCED
* @see #ENHANCEMENT_REQUIRED
* @see #ENHANCEMENT_FAILED
*/
public byte enhancementStatus = ENHANCEMENT_REQUIRED;
/**
* Contains the predefined query key = a query name value = the
* SpeedoPredefinedQuery instance
*
* @see SpeedoPredefinedQuery
*/
public Map name2query;
/**
* Qualifies the status of the no-arg constructor of the persistent class.
*
* @see #NO_NO_ARG_CONSTRUCTOR
* @see #NON_PUBLIC_NO_ARG_CONSTRUCTOR
* @see #PUBLIC_NO_ARG_CONSTRUCTOR
*/
public byte noArgConstructorStatus = NO_NO_ARG_CONSTRUCTOR;
/**
* The main table of the persistent class. External tables are reachable
* from joins.
*
* @see #joinToExtTables
*/
public SpeedoTable mainTable;
/**
* Is the join to reach external tables. It can be null if there is no
* external table.
*/
public SpeedoJoin[] joinToExtTables;
//TODO: Seb --> remove this method for next commit
public String getObjectidClass() {
return identity.objectidClass;
}
/**
* @return the fully qualified name of the class (include the package name)
* the package separator is a dot
*/
public String getFQName() {
if (moPackage.name == null || moPackage.name.length() == 0) {
return name;
} else {
return moPackage.name + "." + name;
}
}
/**
* Transforms a SpeedoClass into a String.
*
* @return the Sting corresponding to the SpeedoClass.
*/
public String toString() {
String s = ("\n class name : " + name + ", identityType : "
+ getIdentityType() + ", objectidClass : " + identity.objectidClass
+ ",version : " + version + ", persistenceCapableSuperClass : " + getSuperClassName());
s += ", \t fields:[";
Iterator it = fields.values().iterator();
while (it.hasNext()) {
s = s + "\t" + it.next().toString();
}
s += "], \t fetchGroups:[";
Iterator it2 = fetchGroups.values().iterator();
while (it2.hasNext()) {
s += "\t" + it2.next().toString();
}
s += "]";
return s;
}
public boolean enhancementFailed() {
return enhancementStatus == ENHANCEMENT_FAILED;
}
public boolean isAlreadyEnhanced() {
return enhancementStatus == ALREADY_ENHANCED;
}
public void setAlreadyEnhanced(boolean v) {
if (v && enhancementStatus == ENHANCEMENT_REQUIRED) {
enhancementStatus = ALREADY_ENHANCED;
}
}
public boolean requireEnhancement() {
return enhancementStatus == ENHANCEMENT_REQUIRED;
}
public void setRequireEnhancement(boolean v) {
if (v && enhancementStatus == ALREADY_ENHANCED) {
enhancementStatus = ENHANCEMENT_REQUIRED;
}
}
/**
* Adds a SpeedoField to the class. Precondition: this field doesn't exist
* in the jdoFields HashMap.
*
* @param field
* field to add.
*/
public void add(Object field) {
SpeedoField f = (SpeedoField) field;
f.moClass = this;
f.number = fieldsCount++;
fields.put(f.name, field);
}
/**
* Adds a SpeedoField to the class.
*
* @param field
* field to add.
* @param failsOnError
* if an error must be thrown or creates a warning.
* @param logger
* logger for writting warn message if necessary.
* @exception SpeedoException
* if the field was already defined into the class.
*/
public void add(Object field, boolean failsOnError, Logger logger)
throws SpeedoException {
SpeedoField f = (SpeedoField) field;
if (fields.containsKey(f.name)) {
if (failsOnError)
throw new SpeedoException("The field " + f.name + " of class "
+ name + " is defined twice.");
else
logger.log(BasicLevel.ERROR, "The field " + f.name
+ " of class " + name + " is defined twice.");
} else {
logger.log(BasicLevel.DEBUG, "Add the field '" + f.name
+ "' in class '" + name + "'.");
f.moClass = this;
fields.put(f.name, field);
}
}
public void setDatastoreIdSequenceName(String sequenceName) {
identity.setDatastoreIdSequenceName(sequenceName);
}
/**
* Adds a SpeedoFetchGroup to the class. Precondition: this fetchgroup
* doesn't exist in the jdoFtechGroups HashMap.
*
* @param fetchGroup
* the fetchgroup to add.
*/
public void addFetchGroup(Object fetchGroup) {
SpeedoFetchGroup fg = (SpeedoFetchGroup) fetchGroup;
fetchGroups.put(fg.name, fetchGroup);
}
/**
* Adds a SpeedoFetchgroup to the class.
*
* @param fetchGroup
* the fetchgroup to add.
* @param failsOnError
* if an error must be thrown or creates a warning.
* @param logger
* logger for writting warn message if necessary.
* @exception SpeedoException
* if the field was already defined into the class.
*/
public void addFetchGroup(Object fetchGroup, boolean failsOnError,
Logger logger) throws SpeedoException {
SpeedoFetchGroup fg = (SpeedoFetchGroup) fetchGroup;
if (fetchGroups.containsKey(fg.name)) {
if (failsOnError)
throw new SpeedoException("The fetchgroup " + fg.name
+ " of class " + name + " is defined twice.");
else
logger.log(BasicLevel.ERROR, "The fetchgroup " + fg.name
+ " of class " + name + " is defined twice.");
} else {
logger.log(BasicLevel.DEBUG, "Add the fetchgroup '" + fg.name
+ "' in class '" + name + "'.");
fetchGroups.put(fg.name, fetchGroup);
}
}
/**
* Computes the field numbers for the fields of this class.
*
* @return the number of fields of this class (including inherited fields).
*/
public int computeFieldNumbers() {
if (fieldsCount != -1) {
return fieldsCount;
}
if (getSuperClassName() == null) {
fieldsCount = 0;
} else {
SpeedoClass jdoSuperClass = moPackage.xmlDescriptor.smi
.getSpeedoClass(getSuperClassName(), moPackage);
if (jdoSuperClass == null) {
throw personality.newUserRuntimeException(
"The persistence-capable-superclass field has a bad value: '"
+ getSuperClassName()
+ "'. See the description of the class '"
+ getFQName() + "' from the file '"
+ getXMLFileName() + "'.");
}
fieldsCount = jdoSuperClass.computeFieldNumbers();
}
Iterator i = fields.values().iterator();
while (i.hasNext()) {
SpeedoField jf = (SpeedoField) i.next();
jf.number = fieldsCount++;
}
return fieldsCount;
}
public String getJormFileName() {
return getFQName().replace('.', File.separatorChar) + ".pd";
}
public boolean generateObjectId() {
return getPkFieldCount() > 1;
}
public int getPkFieldCount() {
if (pkFieldsCount == -1) {
pkFieldsCount = 0;
if (getIdentityType() == SpeedoIdentity.USER_ID
&& (identity.objectidClass == null || identity.objectidClass
.length() == 0)) {
Iterator it = fields.values().iterator();
while (it.hasNext()) {
if (((SpeedoField) it.next()).primaryKey) {
pkFieldsCount++;
}
}
}
}
return pkFieldsCount;
}
public List getPKFields() {
SpeedoClass parent = getSuper();
List idFields;
if (parent == null) {
idFields = new ArrayList();
} else {
idFields = parent.getPKFields();
}
Iterator fieldsIt = fields.values().iterator();
while (fieldsIt.hasNext()) {
SpeedoField f = (SpeedoField) fieldsIt.next();
if (f.primaryKey) {
idFields.add(f);
}
}
return idFields;
}
/**
* Find in the class or in its parent, the unique persistent field marked as
* primary key.
*
* @return the unique pk fields if it exists one, null otherwise.
* @throws SpeedoException
* if there are several persistent fields marked as primary key.
*/
public SpeedoField getUniquePKField() throws SpeedoException {
final ArrayList al = new ArrayList();
for (Iterator it = fields.values().iterator(); it.hasNext();) {
SpeedoField sf = (SpeedoField) it.next();
if (sf.primaryKey) {
al.add(sf);
}
}
final int s = al.size();
if (s == 0) {
if (getSuper() != null) {
return getSuper().getUniquePKField();
}
return null;
} else if (s > 1) {
throw new SpeedoException(
"several fields have been marked as primary-key "
+ al + " in the " + getSourceDesc() + ".");
}
return (SpeedoField) al.get(0);
}
public SpeedoClass getSpeedoClassFromContext(String className) {
return moPackage.xmlDescriptor.smi.getSpeedoClass(className, moPackage);
}
public String getXMLFileName() {
return moPackage.xmlDescriptor.xmlFile;
}
public SpeedoClass getSuper() {
if (getSuperClassName() == null) {
return null;
} else {
return getSpeedoClassFromContext(getSuperClassName());
}
}
public SpeedoField getInheritedField(String name) {
SpeedoClass sc = getSuper();
if (sc == null) {
return null;
}
return sc.getField(name);
}
public SpeedoClass getAncestor() {
SpeedoClass tmp = getSuper();
SpeedoClass ancestor = null;
while (tmp != null && ancestor != tmp) {
ancestor = tmp;
tmp = tmp.getSuper();
}
return ancestor;
}
/**
* Finds a field from its name. The fields can belong this class or an
* ancestor of this class.
*
* @param fieldName
* is the name of a persistent field. the name can be fully
* qualified (ie the field name is prefixed by the class name,
* the separator is a dot or #)
* @return the SpeedoField instance if it has been found., otherwise null
*/
public SpeedoField getField(String fieldName) {
int idx = fieldName.lastIndexOf('.');
SpeedoField sf;
if (idx == -1) {
idx = fieldName.lastIndexOf('#');
}
if (idx != -1) {
// the specified field name is a fully qualified name (include class
// name)
String className = fieldName.substring(0, idx);
String fn = fieldName.substring(idx + 1);
SpeedoClass sc = getSpeedoClassFromContext(className);
if (sc != null) {
// The class has been found, searches in the field in this class
// (recusivity)
return sc.getField(fn);
} else {
return null;
}
}
sf = (SpeedoField) fields.get(fieldName);
if (sf == null && getSuperClassName() != null) {
// try in the parent
sf = getSuper().getField(fieldName);
}
return sf;
}
public SpeedoField getFieldFromColumn(String colname) {
for (Iterator it = fields.values().iterator(); it.hasNext();) {
SpeedoField sf = (SpeedoField) it.next();
if (sf.columns == null) {
continue;
}
for (int i = 0; i < sf.columns.length; i++) {
if (sf.columns[i].table == mainTable
&& colname.equals(sf.columns[i].name)) {
return sf;
}
}
}
return null;
}
public void setIdentityType(byte identityType) {
identity.strategy = identityType;
}
public byte getIdentityType() {
return identity.strategy;
}
public void setSuperClassName(String superClassName) throws SpeedoException {
if (superClassName != null) {
if (inheritance == null) {
inheritance = new SpeedoInheritance();
inheritance.clazz = this;
}
this.inheritance.superClassName = superClassName;
}
}
public String getSuperClassName() {
if (inheritance == null) {
return null;
} else {
return inheritance.superClassName;
}
}
public void addJoin(SpeedoJoin j) {
joinToExtTables = (SpeedoJoin[])
addInArray(j, joinToExtTables, SpeedoJoin[].class);
}
public void removeJoin(SpeedoJoin j) {
joinToExtTables = (SpeedoJoin[])
removeInArray(j, joinToExtTables, SpeedoJoin[].class);
}
public String getSourceDesc() {
StringBuffer sb = new StringBuffer();
sb.append("class '").append(getFQName());
sb.append("' in desc '");
sb.append(moPackage.xmlDescriptor.xmlFile).append("'");
return sb.toString();
}
public String getSourceDescShort() {
StringBuffer sb = new StringBuffer();
sb.append("[").append(getFQName()).append("]");
return sb.toString();
}
public SpeedoJoin getJoin(String tableName) {
if (joinToExtTables != null) {
for (int i = 0; i < joinToExtTables.length; i++) {
if (joinToExtTables[i].extTable.name.equals(tableName)) {
return joinToExtTables[i];
}
}
}
return null;
}
public SpeedoJoin getJoin(String tableName, boolean createifnone) {
SpeedoJoin join = getJoin(tableName);
if (createifnone && join == null) {
join = new SpeedoJoin();
join.mainTable = mainTable;
join.extTable = new SpeedoTable();
join.extTable.name = tableName;
addJoin(join);
}
return join;
}
public int getJoinIndex(SpeedoJoin join) {
if (joinToExtTables != null) {
for (int i = 0; i < joinToExtTables.length; i++) {
if (joinToExtTables[i].equals(join)) {
return i;
}
}
}
return -1;
}
public boolean containsJoin(SpeedoJoin join) {
return getJoinIndex(join) != -1;
}
public SpeedoTable getExtTable(String tableName, boolean createifnone) {
SpeedoJoin join = getJoin(tableName, createifnone);
return join == null ? null : join.extTable;
}
/**
* Look for a column with a given name defined into the given SpeedoClass.
*
* @param colname The name of the column.
* @param mainonly Speficy if we must look only for main table columns.
* @return The column found or null if none.
*/
public SpeedoColumn getColumn(String colname, boolean mainonly) {
for (Iterator it = fields.values().iterator(); it.hasNext();) {
SpeedoField sf = (SpeedoField) it.next();
if (sf.columns == null) {
continue;
}
for (int i = 0; i < sf.columns.length; i++) {
if (mainonly && (sf.columns[i].table != mainTable)) {
continue;
}
if (sf.columns[i].name == null) {
continue;
}
if (colname.equals(sf.columns[i].name)) {
return sf.columns[i];
}
}
}
return null;
}
public List getTableIndexes() {
ArrayList indexes = new ArrayList();
if (mainTable != null) {
indexes.addAll(mainTable.indexes);
}
if (joinToExtTables != null) {
//get the indexes of the joins
for(int i = 0; i < joinToExtTables.length; i++) {
indexes.addAll(joinToExtTables[i].extTable.indexes);
}
}
return indexes;
}
public List getParents() {
SpeedoClass c = getSuper();
if (c == null) {
return Collections.EMPTY_LIST;
}
List parents = new ArrayList();
while(c != null) {
parents.add(0, c);
c = c.getSuper();
}
return parents;
}
}