/**
* Copyright (C) 2001-2005 France Telecom R&D
*/
package org.objectweb.speedo.generation.jorm;
import org.objectweb.jorm.api.PException;
import org.objectweb.jorm.compiler.api.JormCompilerConfigurator;
import org.objectweb.jorm.compiler.api.JormCompilerParameter;
import org.objectweb.jorm.compiler.lib.JormCompiler;
import org.objectweb.jorm.metainfo.api.Class;
import org.objectweb.jorm.metainfo.api.ClassProject;
import org.objectweb.jorm.metainfo.api.GenClassRef;
import org.objectweb.jorm.metainfo.api.Manager;
import org.objectweb.jorm.metainfo.api.Mapping;
import org.objectweb.jorm.metainfo.api.NameDef;
import org.objectweb.jorm.metainfo.api.TypedElement;
import org.objectweb.speedo.api.ExceptionHelper;
import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.api.SpeedoProperties;
import org.objectweb.speedo.generation.api.SpeedoXMLError;
import org.objectweb.speedo.generation.lib.AbstractGeneratorComponent;
import org.objectweb.speedo.lib.Personality;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.metadata.SpeedoField;
import org.objectweb.speedo.metadata.SpeedoIdentity;
import org.objectweb.speedo.metadata.SpeedoXMLDescriptor;
import org.objectweb.util.monolog.api.BasicLevel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* Build the JORM meta information
*
* @author S.Chassande-Barrioz
*/
public class JormMIGenerator extends AbstractGeneratorComponent {
public final static String LOGGER_NAME = SpeedoProperties.LOGGER_NAME
+ ".generation.jorm.migen";
/**
* Jorm classes intended for JORM generation
*/
protected JormCompiler jormcompiler;
public JormMIGenerator(Personality p) {
super(p);
}
/**
* Instanciates and configures partialy a JormCompiler required for the
* .pd parsing and fetching a configured MIManager
*/
public boolean init() throws SpeedoException {
if (scp.getXmldescriptor().isEmpty()) {
return false;
}
logger = scp.loggerFactory.getLogger(LOGGER_NAME);
jormcompiler = new JormCompiler();
JormCompilerParameter jcp = jormcompiler.getCompilerParameter();
JormCompilerConfigurator jcc = jormcompiler.getCompilerConfigurator();
try {
jcc.configure();
jcp.loadConfFile(jcc.getGlobalJormcOptsFile(), jcc.knownMappers());
jcc.setLoggerFactory(scp.loggerFactory);
int idx = scp.mapperName.indexOf('.');
if (idx == -1) {
jcc.removeMapper(scp.mapperName);
jcc.addSubMapper(scp.mapperName, "generic");
} else {
String actualmn = scp.mapperName.substring(0, idx);
jcc.removeMapper(actualmn);
jcc.addSubMapper(actualmn, scp.mapperName.substring(idx + 1));
}
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "jormcOpts file:"
+ jcc.getJormcOptsFile());
Iterator it = jcc.knownMappers();
while (it.hasNext()) {
String mapperName = (String) it.next();
logger.log(BasicLevel.DEBUG, mapperName + " sub mappers: "
+ jcc.getSubMappers(mapperName));
}
}
} catch (PException e) {
logger.log(BasicLevel.ERROR, "Impossible to configure Jorm",
ExceptionHelper.getNested(e));
throw new SpeedoException("Impossible to configure Jorm", e);
}
scp.generatorsContext.put("jormcompiler", jormcompiler);
return true;
}
public String getTitle() {
return "Building Jorm Meta Information...";
}
public void process() throws SpeedoException {
try {
process2();
} catch(PException e) {
throw new SpeedoException("Error during Jorm generation", e);
}
}
/**
* Performs the JORM Meta Information building.
*/
public void process2() throws SpeedoException, PException {
if (scp.getXmldescriptor().isEmpty()) {
return;
}
// The list of .pd files specified by the user
Collection pdfiles = jormcompiler.getCompilerParameter()
.getInputFiles();
if (logger.isLoggable(BasicLevel.DEBUG)) {
for (Iterator it = pdfiles.iterator(); it.hasNext();)
logger.log(BasicLevel.DEBUG, "source file: " + it.next());
}
// The list of SpeedoClass requiring the JMI generation
ArrayList notFoundMOs = new ArrayList();
for (Iterator itDesc = scp.getXmldescriptor().values().iterator(); itDesc
.hasNext();) {
SpeedoXMLDescriptor desc = (SpeedoXMLDescriptor) itDesc.next();
List scs = desc.getSpeedoClasses();
for (int i = 0; i < scs.size(); i++) {
SpeedoClass sc = (SpeedoClass) scs.get(i);
String fn = sc.getJormFileName();
logger.log(BasicLevel.DEBUG, "looking for the file: " + fn);
if (!pdfiles.contains(fn)) {
// No .pd file is availlable, add the SpeedoClass into the
// list
notFoundMOs.add(sc);
}
}
}
// if .pd files are not specified by the user then the jorm meta
// information is generated
jormcompiler.setupLogger();
jormcompiler.setupMIManager();
if (!notFoundMOs.isEmpty()) {
JormMIBuilder jmib = new JormMIBuilder(jormcompiler.getMIManager(),
scp.nmf, logger);
jmib.createMI(notFoundMOs, scp.projectName, scp.mapperName);
}
if (pdfiles.size() > 0) {
// parse the .pd files ==> Collection(jorm MetaObject)
isCompatible();
}
}
/**
* Verifies that all persistent capable field for jdo is described in JORM.
*
* @exception org.objectweb.speedo.generation.api.SpeedoXMLError if JORM an JDO MetaData are not coherent
*/
protected void isCompatible() throws SpeedoException {
List except = new ArrayList();
Manager manager = jormcompiler.getMIManager();
for (Iterator itDesc = scp.getXmldescriptor().values().iterator(); itDesc.hasNext();) {
SpeedoXMLDescriptor desc = (SpeedoXMLDescriptor) itDesc.next();
List scs = desc.getSpeedoClasses();
for (int i=0; i<scs.size(); i++) {
compareClass((SpeedoClass) scs.get(i), manager, except);
}
}
if (!except.isEmpty() && logger.isLoggable(BasicLevel.ERROR))
for (Iterator it = except.iterator(); it.hasNext();)
logger.log(BasicLevel.ERROR, it.next());
}
protected void compareClass(SpeedoClass clas,
Manager manager,
List except) throws SpeedoException {
debug = logger.isLoggable(BasicLevel.DEBUG);
if (debug) {
logger.log(BasicLevel.DEBUG,
"Verify the definition of the class " + clas.getFQName());
}
Class classJorm = manager.getClass(clas.getFQName());
if (classJorm == null) {
throw new SpeedoException("Jorm description of the class '"
+ clas.getFQName() + "' is not availlable");
}
NameDef pName = null;
SpeedoClass sc = clas;
while(sc.getSuperClassName() != null) {
SpeedoClass scl = sc;
sc = sc.moPackage.xmlDescriptor.smi.getSpeedoClass(sc.getSuperClassName(), sc.moPackage);
if (sc == null) {
throw new SpeedoException("Class " + scl.getFQName()
+ " not defined in the jorm meta information");
}
}
Class pclassJorm = manager.getClass(sc.getFQName());
pName = getClassNameDef(pclassJorm);
if (classJorm == null) {
throw new SpeedoXMLError("Class '" + clas.name + "' not defined in JORM metadata");
}
// Comparison from JDO
clas.jormclass = classJorm;
for (Iterator efield = clas.fields.values().iterator(); efield.hasNext();) {
SpeedoField field = (SpeedoField) efield.next();
String fieldName = field.name;
// Fields with this persistent modifier shouldn't stay in JDO Metadata
if (field.persistenceStatus == SpeedoField.NONE)
continue;
TypedElement tElem = classJorm.getTypedElement(fieldName);
if (tElem == null) {
throw new SpeedoXMLError("Field '" + fieldName
+ "' not defined in JORM metadata of the class '"
+ clas.getFQName() + "'");
}
// for application identity key fields have to be compared
if (field.primaryKey) {
boolean found = false;
for (Iterator it = pName.iterateField(); it.hasNext() && !found;) {
found = fieldName.equals(it.next());
}
if (!found) {
throw new SpeedoXMLError("Field '" + fieldName
+ "' not defined in Class NameDef's Fields of the class '"
+ clas.getFQName() + "'");
}
}
if (tElem instanceof GenClassRef && field.jdoTuple == null)
throw new SpeedoXMLError("field '" + fieldName + "' should be a tuple in JDO metadata of the class '" + clas.getFQName() + "'");
}
// Comparison from JORM
for (Iterator jormfield = classJorm.getFields().iterator(); jormfield.hasNext();) {
TypedElement te = (TypedElement) jormfield.next();
String fieldName = te.getName();
if (!clas.fields.containsKey(fieldName)
&& !isContainerIdField(classJorm, te, clas))
throw new SpeedoXMLError("Field '" + fieldName + "' of the class '"
+ clas.getFQName() + "' is not defined in the '"
+ clas.moPackage.xmlDescriptor.xmlFile
+ "' file (found: " + clas.fields.keySet() + ").");
}
// for application identity key fields have to be compared
if (clas.getIdentityType() == SpeedoIdentity.USER_ID) {
SpeedoClass classWithId = clas;
while(classWithId.getSuperClassName() != null) {
classWithId = classWithId.getSpeedoClassFromContext(classWithId.getSuperClassName());
}
boolean found = false;
for (Iterator it = pName.iterateField(); it.hasNext() && !found;) {
String fn = (String) it.next();
SpeedoField sf = (SpeedoField) classWithId.fields.get(fn);
if (sf == null) {
logger.log(BasicLevel.WARN, "Field " + fn + " defined in the identifier of "
+ classWithId.name
+ ".pd file but not availlable in the class (or .pd file).");
} else if (!sf.primaryKey) {
except.add("Field " + fn + " defined in the "
+ classWithId.name
+ ".pd file but is not marked as a primary key field in the "
+ classWithId.moPackage.xmlDescriptor.xmlFile
+ " file.");
}
}
}
}
private boolean isContainerIdField(Class clazz,
TypedElement te,
SpeedoClass sc) throws SpeedoException {
return sc.identity.isDataStore()
&& getClassNameDef(clazz).getNameRef()
.getProjection().containsValue(te.getName());
}
private Mapping getMapping(Class clazz) throws SpeedoException {
ClassProject cp = clazz.getClassProject(scp.projectName);
if (cp == null) {
throw new SpeedoException("No classproject found for the class "
+ clazz.getFQName() + " and the project " + scp.projectName);
}
int idx = scp.mapperName.indexOf('.');
Mapping m = cp.getMapping(idx == -1
? scp.mapperName
: scp.mapperName.substring(0, idx));
if (m == null) {
throw new SpeedoException("No mapping found for the class "
+ clazz.getFQName() + ", the project " + scp.projectName
+ " and the mapper " + scp.mapperName);
}
return m;
}
private NameDef getClassNameDef(Class clazz) throws SpeedoException {
return (NameDef) getMapping(clazz).getClassMapping()
.getIdentifierMapping().getLinkedMO();
}
}