/**
* Copyright (C) 2001-2005 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.generation.parser.ejb;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.objectweb.asm.Type;
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.locale.LocaleHelper;
import org.objectweb.speedo.metadata.SpeedoCallback;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.metadata.SpeedoCollection;
import org.objectweb.speedo.metadata.SpeedoColumn;
import org.objectweb.speedo.metadata.SpeedoDiscriminator;
import org.objectweb.speedo.metadata.SpeedoField;
import org.objectweb.speedo.metadata.SpeedoIdentity;
import org.objectweb.speedo.metadata.SpeedoInheritance;
import org.objectweb.speedo.metadata.SpeedoInheritedField;
import org.objectweb.speedo.metadata.SpeedoJoin;
import org.objectweb.speedo.metadata.SpeedoJoinColumn;
import org.objectweb.speedo.metadata.SpeedoMap;
import org.objectweb.speedo.metadata.SpeedoNoFieldColumn;
import org.objectweb.speedo.metadata.SpeedoNullValue;
import org.objectweb.speedo.metadata.SpeedoPackage;
import org.objectweb.speedo.metadata.SpeedoTable;
import org.objectweb.speedo.metadata.SpeedoXMLDescriptor;
import org.objectweb.speedo.mim.api.HomeItf;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Inheritance;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;
import javax.persistence.PostPersist;
import javax.persistence.PreRemove;
import javax.persistence.PostRemove;
import javax.persistence.PreUpdate;
import javax.persistence.PostUpdate;
import javax.persistence.PostLoad;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.PrimaryKeyJoinColumns;
import javax.persistence.SecondaryTable;
import javax.persistence.SecondaryTables;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;
public class EJBAnnotationParser extends AbstractGeneratorComponent {
public final static String LOGGER_NAME = SpeedoProperties.LOGGER_NAME
+ ".generation.parser.ejb";
private static byte[] ba = new byte[0];
private static Byte[] oba = new Byte[0];
private static char[] bc = new char[0];
private static Character[] obc = new Character[0];
/**
* This is the set of primitive types.
*/
private static final String[] PRIMITIVETYPES = {
Type.getDescriptor(Boolean.TYPE),
Type.getDescriptor(Byte.TYPE),
Type.getDescriptor(Character.TYPE),
Type.getDescriptor(Short.TYPE),
Type.getDescriptor(Integer.TYPE),
Type.getDescriptor(Long.TYPE)
};
/**
* This is the set of supported types for persistent fields.
*/
private static final String[] FIELDTYPES = {
Type.getDescriptor(Boolean.TYPE),
Type.getDescriptor(Boolean.class),
Type.getDescriptor(Byte.TYPE),
Type.getDescriptor(Byte.class),
Type.getDescriptor(Character.TYPE),
Type.getDescriptor(Character.class),
Type.getDescriptor(Short.TYPE),
Type.getDescriptor(Short.class),
Type.getDescriptor(Integer.TYPE),
Type.getDescriptor(Integer.class),
Type.getDescriptor(Long.TYPE),
Type.getDescriptor(Long.class),
Type.getDescriptor(Float.TYPE),
Type.getDescriptor(Float.class),
Type.getDescriptor(Double.TYPE),
Type.getDescriptor(Double.class),
Type.getDescriptor(String.class),
Type.getDescriptor(java.util.Date.class),
Type.getDescriptor(java.util.Calendar.class),
Type.getDescriptor(java.sql.Date.class),
Type.getDescriptor(java.sql.Time.class),
Type.getDescriptor(java.sql.Timestamp.class),
Type.getDescriptor(java.math.BigInteger.class),
Type.getDescriptor(java.math.BigDecimal.class),
Type.getDescriptor(ba.getClass()),
Type.getDescriptor(oba.getClass()),
Type.getDescriptor(bc.getClass()),
Type.getDescriptor(obc.getClass()),
};
/**
* This is the set of supported types for fields that compose a composite
* primary key.
*/
private static final String[] COMPPKFIELDTYPES = {
Type.getDescriptor(Boolean.TYPE),
Type.getDescriptor(Boolean.class),
Type.getDescriptor(Byte.TYPE),
Type.getDescriptor(Byte.class),
Type.getDescriptor(Character.TYPE),
Type.getDescriptor(Character.class),
Type.getDescriptor(Short.TYPE),
Type.getDescriptor(Short.class),
Type.getDescriptor(Integer.TYPE),
Type.getDescriptor(Integer.class),
Type.getDescriptor(Long.TYPE),
Type.getDescriptor(Long.class),
Type.getDescriptor(String.class),
Type.getDescriptor(java.util.Date.class),
Type.getDescriptor(java.sql.Date.class)
};
/**
* This is the set of supported types for Version field.
*/
private static final String[] VERSIONTYPES = {
Type.getDescriptor(Short.TYPE),
Type.getDescriptor(Short.class),
Type.getDescriptor(Integer.TYPE),
Type.getDescriptor(Integer.class),
Type.getDescriptor(Long.TYPE),
Type.getDescriptor(Long.class),
Type.getDescriptor(Timestamp.class)
};
int parsedFiles;
int nbErrors;
LoaderForAnnotAccess annotCL;
public EJBAnnotationParser() {
super(Personality.EJB);
}
static String getter2attribute(String getter) {
return Character.toLowerCase(getter.charAt(3))
+ getter.substring(4, getter.length());
}
public String getTitle() {
return LocaleHelper.getEJBParsingRB().getString("ejbannotpar");
}
/**
* The parser instance is reused at each process method call
*/
// IMPLEMENTATION OF THE GeneratorComponent INTERFACE //
// ---------------------------------------------------//
@Override
public boolean init() throws SpeedoException {
logger = scp.loggerFactory.getLogger(LOGGER_NAME);
logger.log(BasicLevel.DEBUG, "- ejb Annotation Parsing -");
if (scp.xml.isEmpty())
return false;
parsedFiles = 0;
annotCL = new LoaderForAnnotAccess(logger, this.getClass()
.getClassLoader());
annotCL.addDirURL(scp.output);
return true;
}
/**
* Look for annotated EJB entity classes in the class paths and parse them.
*/
@Override
public void process() throws SpeedoException {
logger.log(BasicLevel.DEBUG, scp.xml.size() + " files to parse");
for (Object xmldesc : scp.getXmldescriptor().values()) {
logger.log(BasicLevel.INFO, LocaleHelper.getEJBParsingRB().getString("ejbpfile")
+ ((SpeedoXMLDescriptor) xmldesc).xmlFile);
List<SpeedoClass> parselist = new ArrayList<SpeedoClass>();
for (Object pac : ((SpeedoXMLDescriptor) xmldesc).packages.values()) {
for (Object cl : ((SpeedoPackage) pac).classes.values()) {
Class clazz;
try {
clazz = (Class) Class.forName(
((SpeedoClass) cl).getFQName(), false, annotCL);
} catch (ClassNotFoundException e) {
logger.log(BasicLevel.WARN,
LocaleHelper.getEJBParsingRB().getString("ejbclnotfnd")
+ ((SpeedoClass) cl).getFQName());
continue;
}
if (!isAnnotatedEntityClass(clazz)) {
logger.log(BasicLevel.WARN,
LocaleHelper.getEJBParsingRB().getString("ejbclnotannot")
+ ((SpeedoClass) cl).getFQName());
continue;
}
((SpeedoClass) cl).mainTable = new SpeedoTable();
((SpeedoClass) cl).mainTable.name = "main_table_to_be_defined_later";
((SpeedoClass) cl).inheritance = new SpeedoInheritance();
((SpeedoClass) cl).inheritance.clazz = null;
((SpeedoClass) cl).inheritance.superClassName = null;
Class superc = clazz.getSuperclass();
if (superc == null) { // No superclass
parselist.add((SpeedoClass) cl);
continue;
} else {
((SpeedoClass) cl).inheritance.superClassName = superc.getName();
}
// Look for the superclass in the parse list and insert
// new class after (should parse superclass before
// subclass).
int i = -1;
boolean found = false;
for (SpeedoClass sc : parselist) {
if (! found) {
i++;
}
// Is superc an existing class?
if (superc.getName().equals(sc.getFQName())) {
((SpeedoClass) cl).inheritance.clazz = sc;
found = true;
// i is now fixed for the insertion position
continue;
}
// Is cl a superclass of an existing class?
if (((SpeedoClass) cl).getFQName().equals(sc.inheritance.superClassName)) {
sc.inheritance.clazz = sc;
}
}
if (found) {
parselist.add(i + 1, (SpeedoClass) cl);
} else {
parselist.add(0, (SpeedoClass) cl);
}
}
}
// Remove inheritance info that cannot have been built entirely
for (SpeedoClass sc : parselist) {
if (sc.inheritance.clazz == null) {
sc.inheritance = null;
}
}
// The parsing order as been defined such that super-classes are
// parsed before sub-classes.
nbErrors = 0;
for (SpeedoClass sc : parselist) {
try {
logger.log(BasicLevel.DEBUG, "");
parseEntityClass(Class.forName(sc.getFQName(), false, annotCL), sc);
parsedFiles++;
} catch (ClassNotFoundException e) {
nbErrors++;
logger.log(BasicLevel.ERROR,
"Class not found - should never happen here: "
+ sc.getFQName());
}
}
if (nbErrors > 0) {
if (nbErrors == 1) {
logger.log(BasicLevel.ERROR,
"Abort EJB enhancement: 1 error found while parsing Entity classes defined in '"
+ ((SpeedoXMLDescriptor) xmldesc).xmlFile + "'.");
} else {
logger.log(BasicLevel.ERROR, "Abort EJB enhancement: " + nbErrors
+ " errors found while parsing Entity classes defined in '"
+ ((SpeedoXMLDescriptor) xmldesc).xmlFile + "'.");
}
throw new SpeedoException("EJB parsing error.");
}
}
}
/**
* Determine if this is an EJB3 annotated entity class.
*/
private boolean isAnnotatedEntityClass(Class clazz)
throws SpeedoException {
Entity e = (Entity) clazz.getAnnotation(Entity.class);
return (e != null);
}
/**
* Parse an EJB3 Entity class.
*
* @param c The Class to be parsed.
* @param sc The SpeedoClass to be constructed.
* @throws SpeedoException
*/
private void parseEntityClass(Class c, SpeedoClass sc)
throws SpeedoException {
Entity e = (Entity) c.getAnnotation(Entity.class);
if (e == null) { // This is not an Entity class
throw new SpeedoException(c.getName()
+ ": should be an annotated EJB3 entity class!");
}
logger.log(BasicLevel.DEBUG, "Parse annotation of class: "
+ c.getName());
// name of the Entity when used in a query
sc.nameForQuery = e.name();
// kind of access to persistent information (variables ou
// accessors)
for (Class itf : c.getInterfaces()) {
if (itf == Serializable.class) {
sc.isSerializable = true;
break;
}
}
// ---------------------------------------------------------------------
// Define name for Primary Table (parse definition later...)
Table t = (Table) c.getAnnotation(Table.class);
if (t == null) { // There is no Table annotation: use defaults
sc.mainTable.name = sc.name;
} else {
sc.mainTable.name = t.name();
}
// ---------------------------------------------------------------------
// Parse the attribute information for retrieving mapping information
if (c.getAnnotation(IdClass.class) != null) {
sc.identity = new SpeedoIdentity();
sc.identity.objectidJClass = ((IdClass) c.getAnnotation(IdClass.class)).value();
sc.identity.objectidClass = sc.identity.objectidJClass.getName();
sc.identity.strategy = SpeedoIdentity.USER_ID;
parsePkClass(sc);
}
// look for persistent attributes represented by methods
// (getters/setters)
for (Method m : c.getMethods()) {
if (!isRelevantAttribute(m, c)) {
continue;
}
SpeedoField sf = new SpeedoField();
sf.name = getter2attribute(m.getName());
// sf.visibility;
sf.moClass = sc;
sc.fields.put(sf.name, sf);
logger.log(BasicLevel.DEBUG, "New SpeedoField for EJB property: "
+ sf.name + ", TYPE: " + sf.type);
parseManyToOne(m.getAnnotation(ManyToOne.class), sc, sf);
parseOneToOne(m.getAnnotation(OneToOne.class), sc, sf);
parseOneToMany(m.getAnnotation(OneToMany.class), sc, sf);
parseManyToMany(m.getAnnotation(ManyToMany.class), sc, sf);
parseType((Class) m.getReturnType(), m.getGenericReturnType(), sc,
sf);
if (sf.relationType == SpeedoField.NO_BI_RELATION) {
excludeAnnotation(new Object[] {
m.getAnnotation(JoinColumns.class),
m.getAnnotation(JoinColumn.class),
m.getAnnotation(JoinTable.class) }, sc, sf.name,
"property");
parseVersion(m.getAnnotation(Version.class), sc, sf);
parseColumn(m.getAnnotation(Column.class), sc, sf);
parseBasic(m.getAnnotation(Basic.class), sc, sf, true);
parseLob(m.getAnnotation(Lob.class), sc, sf, true);
if (m.getAnnotation(Embedded.class) != null) {
parseEmbeddedOverriding(
(AttributeOverride) m.getAnnotation(AttributeOverride.class),
(AttributeOverrides) m.getAnnotation(AttributeOverrides.class),
sc, sf);
}
} else {
excludeAnnotation(new Object[] {
m.getAnnotation(Version.class),
m.getAnnotation(Column.class),
m.getAnnotation(Basic.class),
m.getAnnotation(Lob.class) }, sc, sf.name,
"association property");
if (m.getAnnotation(JoinColumns.class) != null) {
if (m.getAnnotation(JoinColumn.class) != null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": cannot define both 'JoinColumns' and'JoinColumn' annotation for property ("
+ sf.name + ").");
} else {
parseJoinColumns(
m.getAnnotation(JoinColumns.class).value(),
sc, sf);
}
} else {
if (m.getAnnotation(JoinColumn.class) != null) {
parseJoinColumns(
new JoinColumn[] { m.getAnnotation(JoinColumn.class) },
sc, sf);
}
}
}
parseEmbeddedId(m.getAnnotation(EmbeddedId.class), sc, sf);
if (parseId(m.getAnnotation(Id.class), sc, sf)) {
// ---------------------------------------------------------------------
// In case of Long identifier, try parsing Id generator
parseGeneratedValue((GeneratedValue) m.getAnnotation(GeneratedValue.class), sc);
} else {
if (m.getAnnotation(GeneratedValue.class) != null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": can only associate an identifier generator to a Long ID - ignored!");
}
}
}
// look for persistent attributes represented by variables
for (Field f : c.getDeclaredFields()) {
if (!isRelevantAttribute(f, c)) {
continue;
}
SpeedoField sf = new SpeedoField();
sf.name = f.getName();
// sf.visibility;
sf.moClass = sc;
sc.fields.put(sf.name, sf);
logger.log(BasicLevel.DEBUG, "New SpeedoField for EJB field: "
+ sf.name + ", TYPE: " + sf.type);
parseManyToOne(f.getAnnotation(ManyToOne.class), sc, sf);
parseOneToOne(f.getAnnotation(OneToOne.class), sc, sf);
parseOneToMany(f.getAnnotation(OneToMany.class), sc, sf);
parseManyToMany(f.getAnnotation(ManyToMany.class), sc, sf);
parseType((Class) f.getType(), f.getGenericType(), sc, sf);
if (sf.relationType == SpeedoField.NO_BI_RELATION) {
excludeAnnotation(new Object[] {
f.getAnnotation(JoinColumns.class),
f.getAnnotation(JoinColumn.class),
f.getAnnotation(JoinTable.class) }, sc, sf.name,
"field");
parseVersion(f.getAnnotation(Version.class), sc, sf);
parseColumn(f.getAnnotation(Column.class), sc, sf);
parseBasic(f.getAnnotation(Basic.class), sc, sf, false);
parseLob(f.getAnnotation(Lob.class), sc, sf, false);
if (f.getAnnotation(Embedded.class) != null) {
parseEmbeddedOverriding(
(AttributeOverride) f.getAnnotation(AttributeOverride.class),
(AttributeOverrides) f.getAnnotation(AttributeOverrides.class),
sc, sf);
}
} else {
excludeAnnotation(new Object[] {
f.getAnnotation(Version.class),
f.getAnnotation(Column.class),
f.getAnnotation(Basic.class),
f.getAnnotation(Lob.class) }, sc, sf.name,
"association field");
if (f.getAnnotation(JoinColumns.class) != null) {
if (f.getAnnotation(JoinColumn.class) != null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": cannot define both 'JoinColumns' and'JoinColumn' annotation at the same time for field ("
+ sf.name + ").");
} else {
parseJoinColumns(f.getAnnotation(JoinColumns.class)
.value(), sc, sf);
}
} else {
if (f.getAnnotation(JoinColumn.class) != null) {
parseJoinColumns(new JoinColumn[] { f
.getAnnotation(JoinColumn.class) }, sc, sf);
}
}
}
parseEmbeddedId(f.getAnnotation(EmbeddedId.class), sc, sf);
if (parseId(f.getAnnotation(Id.class), sc, sf)) {
// ---------------------------------------------------------------------
// In case of Long identifier, try parsing Id generator
parseGeneratedValue((GeneratedValue) f.getAnnotation(GeneratedValue.class), sc);
} else {
if (f.getAnnotation(GeneratedValue.class) != null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": can only associate an identifier generator to a Long ID - ignored!");
}
}
}
// Verify that a mapping has been defined for each field when needed
for (SpeedoField sf : (Collection<SpeedoField>) sc.fields.values()) {
if (sf.mappedByReversefield) {
sf.relationType = SpeedoField.NO_BI_RELATION;
continue;
}
if (sf.relationType == SpeedoField.NO_BI_RELATION) {
continue;
}
if (sf.relationType == SpeedoField.MANY_REFERENCE) {
if (sf.join == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": a mapping must be associated with the n-ary association field ("
+ sf.name + ").");
}
} else { // sf.relationType == SpeedoField.ONE_REFERENCE
if (sf.columns.length == 0) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": a mapping must be associated with the unary association field ("
+ sf.name + ").");
}
}
// The real type of relation will be calculated later (@see ReverseFieldAdder).
sf.relationType = SpeedoField.NO_BI_RELATION;
}
// Verify that there is an identifier definition
if (sc.getPKFields().size() == 0) {
SpeedoClass sch = sc.getSuper();
while (sch != null) {
if (sch.getPKFields().size() != 0) {
break;
}
sch = sch.getSuper();
}
if (sch == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": no identity defined for the class.");
}
}
// ---------------------------------------------------------------------
// Parse table definitions
// - Primary table
if (t != null) {
parseTable(t, sc, sc.mainTable);
}
// - Secondary tables
SecondaryTable st = (SecondaryTable) c.getAnnotation(SecondaryTable.class);
SecondaryTables sts = (SecondaryTables) c.getAnnotation(SecondaryTables.class);
if (sts != null) {
if (st != null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": cannot define both 'SecondaryTable' and 'SecondaryTables' annotations at the same time.");
} else {
for (SecondaryTable st2 : sts.value()) {
parseSecondaryTable(st2, sc);
}
}
} else {
if (st != null) {
parseSecondaryTable(st, sc);
}
}
// Parse inheritance strategy information
parseDiscriminatorColumn(c, sc);
parseDiscriminatorValue(c, sc);
parseInheritanceJoin(c, sc);
parseInheritance(c, sc);
if (sc.identity == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": an identifier must be defined for this Entity class.");
}
// Parse attributes overriding
AttributeOverride ao = (AttributeOverride) c.getAnnotation(AttributeOverride.class);
AttributeOverrides aos = (AttributeOverrides) c.getAnnotation(AttributeOverrides.class);
parseAttributeOverriding(ao, aos, sc);
for (Method m : c.getMethods()) {
ao = (AttributeOverride) m.getAnnotation(AttributeOverride.class);
aos = (AttributeOverrides) m .getAnnotation(AttributeOverrides.class);
if (m.getAnnotation(Embedded.class) == null) {
parseAttributeOverriding(ao, aos, sc);
}
}
for (Field f : c.getDeclaredFields()) {
ao = (AttributeOverride) f.getAnnotation(AttributeOverride.class);
aos = (AttributeOverrides) f.getAnnotation(AttributeOverrides.class);
if (f.getAnnotation(Embedded.class) == null) {
parseAttributeOverriding(ao, aos, sc);
}
}
// ---------------------------------------------------------------------
// Parse the callback associated to Entity lifecycle:
// could be present inside the Entity class itself or
// inside associated EntityListener classes.
EntityListeners els = (EntityListeners) c.getAnnotation(EntityListeners.class);
if (els != null) {
// Parse the listener classes for callbacks
for (Class elc : els.value()) {
parseCallBacks(elc.getMethods(), sc, elc);
}
}
// Parse the class for callbacks
parseCallBacks(c.getMethods(), sc, null);
}
/**
* Verify if it is a relevant attribute definition to be parsed.
*
* @param methorfield The attribute definition (Method or Field).
* @param c The Java class under parsing.
* @return true if it must be parsed.
*/
private boolean isRelevantAttribute(Object methorfield, Class c) {
if (methorfield instanceof Field) {
Field f = (Field) methorfield;
if ((f.getAnnotation(Column.class) == null)
&& (f.getAnnotation(Basic.class) == null)
&& (f.getAnnotation(OneToOne.class) == null)
&& (f.getAnnotation(OneToMany.class) == null)
&& (f.getAnnotation(ManyToOne.class) == null)
&& (f.getAnnotation(ManyToMany.class) == null)
&& (f.getAnnotation(Id.class) == null)
&& (f.getAnnotation(JoinColumn.class) == null)
&& (f.getAnnotation(Version.class) == null)) {
return false;
}
if (f.getDeclaringClass() != c) {
return false;
}
if ((f.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT) {
return false;
}
if (f.getAnnotation(Transient.class) != null) {
return false;
}
} else {
Method m = (Method) methorfield;
if ((m.getAnnotation(Column.class) == null)
&& (m.getAnnotation(Basic.class) == null)
&& (m.getAnnotation(OneToOne.class) == null)
&& (m.getAnnotation(OneToMany.class) == null)
&& (m.getAnnotation(ManyToOne.class) == null)
&& (m.getAnnotation(ManyToMany.class) == null)
&& (m.getAnnotation(Id.class) == null)
&& (m.getAnnotation(JoinColumn.class) == null)
&& (m.getAnnotation(Version.class) == null)) {
return false;
}
if (m.getDeclaringClass() != c) {
return false;
}
if (!m.getName().startsWith("get")) { // Could it be a getter
return false;
}
try {
c.getDeclaredMethod(m.getName().replaceFirst("g", "s"),
new Class[] {m.getReturnType()});
} catch (NoSuchMethodException e1) {
return false;
}
if (m.getParameterTypes().length != 0) { // Getter => no arg
return false;
}
if (m.getReturnType() == null) { // Getter => return type must exist
return false;
}
if (m.getAnnotation(Transient.class) != null) {
return false;
}
}
return true;
}
/**
* Parse the DiscriminatorColumn annotation associated with an Entity class.
*
* @param c The Entity class to be parsed.
* @param sc The SpeedoClass under construction.
*/
private void parseDiscriminatorColumn(Class c, SpeedoClass sc) {
DiscriminatorColumn a = (DiscriminatorColumn) c.getAnnotation(DiscriminatorColumn.class);
if (a == null) {
return;
}
if (sc.inheritance == null) {
// This is a root class in an inheritance hierarchy
sc.inheritance = new SpeedoInheritance();
sc.inheritance.clazz = sc;
sc.inheritance.superClassName = null;
sc.inheritance.discriminator = new SpeedoDiscriminator();
String cn = a.name();
if (cn.equals("")) {
cn = "TYPE"; // Default name of the discriminator column
}
SpeedoNoFieldColumn snofc = new SpeedoNoFieldColumn();
SpeedoColumn scol = sc.getColumn(cn, true);
if (scol == null) {
// There is no existing column: create one
scol = new SpeedoColumn();
scol.name = cn;
scol.allowNull = false;
if (! a.columnDefinition().equals("")) {
scol.sqlType = a.columnDefinition();
}
scol.length = a.length();
scol.table = sc.mainTable;
}
snofc.column = scol;
switch (a.discriminatorType()) {
case STRING:
snofc.type = Type.getDescriptor(String.class);
break;
case CHAR:
snofc.type = Type.getDescriptor(Character.TYPE);
break;
case INTEGER:
snofc.type = Type.getDescriptor(Integer.TYPE);
break;
}
sc.inheritance.discriminator.elements.add(snofc);
} else {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": discriminator column can only be defined in the root class of an inheritance hierarchy!");
return;
}
}
/**
* Parse the DiscriminatorValue annotation associated with an Entity class.
*
* @param c The Entity class to be parsed.
* @param sc The SpeedoClass under construction.
*/
private void parseDiscriminatorValue(Class c, SpeedoClass sc) {
DiscriminatorValue a = (DiscriminatorValue) c.getAnnotation(DiscriminatorValue.class);
if (a == null) {
return;
}
SpeedoNoFieldColumn snofc = (SpeedoNoFieldColumn) sc.getAncestor().inheritance.discriminator.elements.get(0);
if (a.value().equals("")) {
sc.inheritance.discriminatorValues.put(snofc, SpeedoInheritance.SPEEDO_DEFAULT_DISCRIMINENT_VALUE);
} else {
sc.inheritance.discriminatorValues.put(snofc, a.value());
}
}
/**
* Parse the inheritance join in case of JOINED inheritance mapping
* strategy.
*
* @param c The Entity class to be parsed.
* @param sc The SpeedoClass under construction.
*/
private void parseInheritanceJoin(Class c, SpeedoClass sc) {
if (sc.inheritance == null) {
return;
}
PrimaryKeyJoinColumn a1 = (PrimaryKeyJoinColumn) c.getAnnotation(PrimaryKeyJoinColumn.class);
PrimaryKeyJoinColumns a2 = (PrimaryKeyJoinColumns) c.getAnnotation(PrimaryKeyJoinColumns.class);
if ((a1 == null) && (a2 == null)) {
return;
}
if ((a1 != null) && (a2 != null)) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": can only support a unique join description - two found (a join column and a set of join columns)!");
return;
}
if (a2.value().length == 1) {
a1 = a2.value()[0];
}
sc.inheritance.join = new SpeedoJoin();
sc.inheritance.join.mainTable = sc.mainTable;
sc.inheritance.join.extTable = sc.getSuper().mainTable;
// First parse PrimaryKeyJoinColumn
if (a1 != null) {
SpeedoJoinColumn jcol = new SpeedoJoinColumn();
jcol.column = sc.getColumn(a1.name(), true);
if (jcol.column == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": inheritance join define with a column that does not exist - ("
+ a1.name() + ") not found!");
return;
}
if (a1.referencedColumnName().equals("")) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": inheritance join define with no column associated in superclass!");
return;
}
jcol.targetColumn = a1.referencedColumnName();
sc.inheritance.join.columns.add(jcol);
return;
}
// Now parse PrimaryKeyJoinColumns: the PrimaryKeyJoinColmun from the "pkjcs" array
for (PrimaryKeyJoinColumn pkjc : a2.value()) {
SpeedoJoinColumn jcol = new SpeedoJoinColumn();
jcol.column = sc.getColumn(pkjc.name(), true);
if (jcol.column == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": inheritance join define with a column that does not exist - ("
+ pkjc.name() + ") not found!");
continue;
}
// Define the referenced column in the super class of this jcol
if (pkjc.referencedColumnName().equals("")) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": inheritance join define with no column associated in superclass!");
continue;
}
jcol.targetColumn = pkjc.referencedColumnName();
sc.inheritance.join.columns.add(jcol);
}
}
/**
* Parse the Inheritance annotation associated with an Entity class.
*
* @param c The Entity class to be parsed.
* @param sc The SpeedoClass under construction.
*/
private void parseInheritance(Class c, SpeedoClass sc) {
Inheritance a = (Inheritance) c.getAnnotation(Inheritance.class);
if (a == null) {
return;
}
if (sc.inheritance == null) {
// This is a root class in an inheritance hierarchy
sc.inheritance = new SpeedoInheritance();
sc.inheritance.clazz = sc;
sc.inheritance.superClassName = null;
}
switch (a.strategy()) {
case SINGLE_TABLE: // filtered inheritance mapping
SpeedoNoFieldColumn snofc;
if (sc.inheritance.join != null) {
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": no join column definition required for SINGLE_TABLE strategy inheritance - ignored!");
sc.inheritance.join = null;
}
if (sc.getAncestor() == null) {
// This is the root class of the inheritance hierarchy
sc.inheritance.strategy = SpeedoInheritance.STRATEGY_NEW_TABLE;
if (sc.inheritance.discriminator == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": SINGLE_TABLE strategy inheritance requires discriminator description - none found!");
return;
}
snofc = (SpeedoNoFieldColumn) sc.inheritance.discriminator.elements.get(0);
} else {
sc.inheritance.strategy = SpeedoInheritance.STRATEGY_SUPERCLASS_TABLE;
if (sc.inheritance.discriminator != null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": discriminator column should be defined only in root class of inheritance hiereachy - ignored!");
sc.inheritance.discriminator = null;
}
snofc = (SpeedoNoFieldColumn) sc.getAncestor().inheritance.discriminator.elements.get(0);
if (sc.identity != null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": identity cannot be redefined into subclasses when SINGLE_TABLE strategy is used - ignored!");
}
sc.identity = sc.getAncestor().identity;
}
sc.inheritance.discriminatorValues = new HashMap();
break;
case TABLE_PER_CLASS: // horizontal inheritance mapping
if (sc.inheritance.join != null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": no join column definition required for TABLE_PER_CLASS inheritance strategy - ignored!");
sc.inheritance.join = null;
}
sc.inheritance.strategy = SpeedoInheritance.STRATEGY_NEW_TABLE;
if (sc.inheritance.discriminator != null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": no discriminator column definition required for TABLE_PER_CLASS inheritance strategy - ignored!");
sc.inheritance.discriminator = null;
}
// Define attribute mapping for attributes of the superclasses
SpeedoClass inhsc = sc.getSuper();
while (inhsc != null) {
for (Object inhsf : inhsc.fields.values()) {
if (inhsf instanceof SpeedoInheritedField) {
continue;
}
SpeedoInheritedField sif =
sc.inheritance.newSpeedoInheritedField((SpeedoField)inhsf);
for (SpeedoColumn scol : sif.inheritedField.columns) {
SpeedoColumn nscol = (SpeedoColumn) scol.clone();
nscol.table = sc.mainTable;
sif.addColumn(nscol);
}
}
inhsc = inhsc.getSuper();
}
break;
case JOINED: // vertical inheritance mapping
sc.inheritance.strategy = SpeedoInheritance.STRATEGY_NEW_TABLE;
if (sc.getAncestor() == null) {
// This is the root class of the inheritance hierarchy
if (sc.inheritance.join != null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": no join column definition required for root class for JOINED inheritance strategy - ignored!");
sc.inheritance.join = null;
}
if (sc.inheritance.discriminator == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": JOINED strategy inheritance requires discriminator description - none found!");
return;
}
snofc = (SpeedoNoFieldColumn) sc.inheritance.discriminator.elements.get(0);
} else {
if (sc.inheritance.join == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": JOINED strategy inheritance requires a join description - none found!");
return;
}
if (sc.inheritance.discriminator != null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": discriminator column should be defined only in root class of inheritance hiereachy - ignored!");
sc.inheritance.discriminator = null;
}
snofc = (SpeedoNoFieldColumn) sc.getAncestor().inheritance.discriminator.elements.get(0);
sc.identity = sc.getAncestor().identity;
}
sc.inheritance.discriminatorValues = new HashMap();
break;
}
}
static final Class[] OBJSIGN = {Object.class};
static final Class[] EMPTYSIGN = {};
/**
* Verify if the given Class may be used as an EJB3 PK class.
*
* @param sc The SpeedoClass under construction. The identity field
* defines a PK class that has be verified as compliant wrt
* EJB3.
* @return true if the PK class is conform.
*/
private boolean parsePkClass(SpeedoClass sc) {
if ((sc.identity.objectidJClass.getModifiers() & Member.PUBLIC) != Member.PUBLIC) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") must be public.");
return false;
}
try {
sc.identity.objectidJClass.getConstructor(EMPTYSIGN);
} catch (NoSuchMethodException e) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") must have a public constructor with no arg.");
return false;
}
if (! Serializable.class.isAssignableFrom(sc.identity.objectidJClass)) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") must be Serializable.");
return false;
}
Method meq, mha;
try {
meq = sc.identity.objectidJClass.getDeclaredMethod("equals", OBJSIGN);
} catch (NoSuchMethodException e) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") must implement 'equals'.");
return false;
}
try {
mha = sc.identity.objectidJClass.getDeclaredMethod("hashCode", EMPTYSIGN);
} catch (NoSuchMethodException e) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") must implement 'hashCode'.");
return false;
}
for (Method m : sc.identity.objectidJClass.getDeclaredMethods()) {
if ((m == meq) || (m == mha)) {
continue;
}
if (m.getName().startsWith("get")) {
if (m.getParameterTypes().length != 0) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") define primary key getter with arguments ("
+ m.getName() + ").");
continue;
}
if (! isValidType(Type.getDescriptor(m.getReturnType()), COMPPKFIELDTYPES)) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") define primary key getter with invalid type ("
+ m.getName() + ").");
continue;
}
} else if (m.getName().startsWith("set")) {
if (m.getParameterTypes().length != 1) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") not define primary key setter one argument ("
+ m.getName() + ").");
continue;
}
if (m.getReturnType() != Void.TYPE) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") define primary key setter with non void return type ("
+ m.getName() + ").");
continue;
}
if (! isValidType(Type.getDescriptor(m.getParameterTypes()[0]), COMPPKFIELDTYPES)) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") define primary key setter with invalid argument type ("
+ m.getName() + ").");
continue;
}
}
}
for (Field f : sc.identity.objectidJClass.getDeclaredFields()) {
if (! isValidType(Type.getDescriptor(f.getType()), COMPPKFIELDTYPES)) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": PK class ("
+ sc.identity.objectidJClass.getName() + ") define primary key field with invalid type ("
+ f.getName() + ").");
}
}
return true;
}
/**
* Parse the type associated with a field.
* "type" field may temporarily stores type name of target association entity
* for verification purpose at type parsing.
*
* @param t The type that has to be parsed.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
*/
private void parseType(Class t, java.lang.reflect.Type gt, SpeedoClass sc, SpeedoField sf) {
if (t == null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": unknown type for element of collection for field ("
+ sf.name + ").");
return;
}
SpeedoClass scf = scp.smi.getSpeedoClass(t.getName());
if (scf != null) {
// This is en Entity class.
sf.relationType = SpeedoField.ONE_REFERENCE;
if (sf.type != null) { // defined at association definition time
if (! sf.type.equals(t.getName())) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": type for field ("
+ sf.name + ") is different from the one defined in association - association type ignored.");
}
}
sf.type = Type.getDescriptor(t);
return;
}
if (isValidType(Type.getDescriptor(t), FIELDTYPES) || t.isEnum()) {
// This a basic valid type or an enum type. Nothing to do.
sf.type = Type.getDescriptor(t);
return;
}
// Verifies if it is a Collection or a Serializable type.
if (Set.class.isAssignableFrom(t)
|| List.class.isAssignableFrom(t)
|| Collection.class.isAssignableFrom(t)) {
// This is a valid type: a Set, List or Collection of Entity class.
sf.jdoTuple = new SpeedoCollection();
sf.jdoTuple.moField = sf;
if (t == gt) {
// No generic type defined.
if (sf.type == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": unknown type for element of Set/List/Collection for field ("
+ sf.name + ").");
return;
}
((SpeedoCollection) sf.jdoTuple).elementType = sf.type;
sf.type = Type.getDescriptor(t);
return;
}
// The Java generic type is completely defined.
sf.relationType = SpeedoField.MANY_REFERENCE;
SpeedoClass escf = scp.smi.getSpeedoClass(((Class)((ParameterizedType) gt).getActualTypeArguments()[0]).getName());
if (escf == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": element of collection should belong to an Entity class (found "
+ Type.getDescriptor((Class)((ParameterizedType) gt).getActualTypeArguments()[0])
+ ") for field (" + sf.name + ").");
return;
}
if (! sf.type.equals(escf.name)) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": type for element of collection field ("
+ sf.name + ") is different from the one defined in association - association type ignored.");
}
((SpeedoCollection) sf.jdoTuple).elementType = ((Class)((ParameterizedType) gt).getActualTypeArguments()[0]).getName();
sf.type = Type.getDescriptor(t);
return;
}
if (Map.class.isAssignableFrom(t)) {
// This is a Map valid type.
sf.jdoTuple = new SpeedoMap();
sf.jdoTuple.moField = sf;
if (t == gt) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": unknown type for element of Map for field ("
+ sf.name + ").");
return;
}
// The Java generic type is completely defined.
sf.relationType = SpeedoField.MANY_REFERENCE;
SpeedoClass escf = scp.smi.getSpeedoClass(((Class)((ParameterizedType) gt).getActualTypeArguments()[1]).getName());
if (escf == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": element of Map should belong to an Entity class (found "
+ Type.getDescriptor((Class)((ParameterizedType) gt).getActualTypeArguments()[1])
+ ") for field (" + sf.name + ").");
return;
}
((SpeedoMap) sf.jdoTuple).keyType = ((Class)((ParameterizedType) gt).getActualTypeArguments()[0]).getName();
((SpeedoMap) sf.jdoTuple).valueType = ((Class)((ParameterizedType) gt).getActualTypeArguments()[1]).getName();
sf.type = Type.getDescriptor(t);
return;
}
if (Serializable.class.isAssignableFrom(t)) {
// This is a Serializable valid type. Nothing to do.
sf.type = Type.getDescriptor(t);
return;
}
// All other types are not supported.
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": unsupported type (" + Type.getDescriptor(t)
+ ") for field (" + sf.name + ").");
}
/**
* Parse the version field associated to a class for supporting database
* optimistic locking policy.
*
* @param a The Version annotation to be parsed.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField to be associated as the version field.
*/
private void parseVersion(Version a, SpeedoClass sc, SpeedoField sf) {
if (a == null) {
return;
}
if (sc.versionField == null) {
if (! isValidType(sf.type, VERSIONTYPES)) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to define a field version ("
+ sf.name + ") - unsupported type ("
+ sf.type + ").");
}
sc.versionField = sf;
} else {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": a field version has already been defined ("
+ sc.versionField.name + ") - cannot associate a second one ("
+ sf.name + ").");
}
}
/**
* Parse field meta-information from the Column annotation.
*
* @param col The annotation to extract information from.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
*/
private void parseColumn(Column col, SpeedoClass sc, SpeedoField sf) {
SpeedoColumn scol = new SpeedoColumn();
if (col == null) {
if (sf.relationType != SpeedoField.NO_BI_RELATION) {
return;
}
scol.name = sf.name;
scol.table = sc.mainTable;
sf.addColumn(scol);
logger.log(BasicLevel.DEBUG, sc.getSourceDescShort()
+ ": add a default column definition for field '"
+ sf.name + "'.");
return;
} else {
if (sf.relationType != SpeedoField.NO_BI_RELATION) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": try to define a column associated with an association field - ignored.");
return;
}
}
if (!col.table().equals("")) {
if (col.table().equals(sc.mainTable.name)) {
scol.table = sc.mainTable;
} else {
scol.table = sc.getExtTable(col.table(), true);
}
} else {
scol.table = sc.mainTable;
}
if (col.unique()) {
ArrayList<SpeedoColumn> cols = new ArrayList<SpeedoColumn>(1);
cols.add(scol);
scol.table.addUniqueConstraint(cols);
logger.log(BasicLevel.DEBUG, sc.getSourceDescShort()
+ ": add a unique constraint ["
+ cols
+ "] to table (" + scol.table.name + ").");
}
if (col.name().equals("")) {
scol.name = sf.name;
} else {
scol.name = col.name();
}
scol.allowNull = col.nullable();
// TODO: have a clean assignment of Speedo length/scale wrt
// EJB3 length/precision/scale
scol.length = col.length();
scol.scale = col.scale();
// ? = col.precision();
scol.insertable = col.insertable();
scol.updatable = col.updatable();
if (!col.columnDefinition().equals("")) {
scol.sqlType = col.columnDefinition();
}
sf.addColumn(scol);
logger.log(BasicLevel.DEBUG, sc.getSourceDescShort()
+ ": add a column definition for field '"
+ sf.name + "' (tabName=" + scol.table.name
+ ", colName=" + scol.name + ").");
}
/**
* Parse field meta-information from the Column annotation.
*
* @param col The annotation to extract information from.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoInheritedField under construction.
*/
private void parseOverrideColumn(Column col, SpeedoClass sc, SpeedoInheritedField sf) {
// TODO: code copy from parseColumn->implement!!
if (col == null) {
return;
}
SpeedoColumn scol = new SpeedoColumn();
if (!col.table().equals("")) {
scol.table = sc.getExtTable(col.table(), true);
} else {
scol.table = sc.mainTable;
}
if (col.unique()) {
ArrayList<SpeedoColumn> cols = new ArrayList<SpeedoColumn>(1);
cols.add(scol);
scol.table.addUniqueConstraint(cols);
logger.log(BasicLevel.DEBUG, sc.getSourceDescShort()
+ ": add a unique constraint ["
+ cols
+ "] to table (" + scol.table.name + ").");
}
if (col.name().equals("")) {
scol.name = sf.name;
} else {
scol.name = col.name();
}
scol.allowNull = col.nullable();
// TODO: have a clean assignment of Speedo length/scale wrt
// EJB3 length/precision/scale
scol.length = col.length();
scol.scale = col.scale();
// ? = col.precision();
scol.insertable = col.insertable();
scol.updatable = col.updatable();
if (!col.columnDefinition().equals("")) {
scol.sqlType = col.columnDefinition();
}
sf.addColumn(scol);
logger.log(BasicLevel.DEBUG, sc.getSourceDescShort()
+ ": add a column definition overriding for field '"
+ sf.name + "' (tabName=" + scol.table.name
+ ", colName=" + scol.name + ").");
}
/**
* Parse an Id annotation to define the SpeedoIdentifier associated with a
* SpeedoClass.
*
* @param ida The Id annotation.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
* @return true if the parsed Id is a correctly defined unique field
* identifier with a Long type.
*/
private boolean parseId(Id ida, SpeedoClass sc, SpeedoField sf) {
if (ida == null) {
return false;
}
if ((sc.identity != null) && (sc.identity.objectidJClass != null)) {
// There is an IdClass: verify field conformance
try {
Field f = sc.identity.objectidJClass.getDeclaredField(sf.name);
if (sf.type.equals(Type.getDescriptor(f.getType()))) {
sf.primaryKey = true;
return false;
}
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": try to define field (" + sf.name
+ ") as an Id field - has not the same type in IdClass ("
+ sc.identity.objectidJClass.getName() + ").");
} catch (NoSuchFieldException e) {
// Field does not exist:
}
// There is no public field in PK class. Look for public accessors
// (getter & setter) for accessing this field.
try {
Method m = sc.identity.objectidJClass.getDeclaredMethod(toJavaBean("get", sf.name),
EMPTYSIGN);
if (! sf.type.equals(Type.getDescriptor(m.getReturnType()))) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to define field getter (" + m.getName()
+ ") for an Id field - has not the same type in IdClass ("
+ sc.identity.objectidJClass.getName() + ").");
return false;
}
} catch (NoSuchMethodException e) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to find field getter ("
+ toJavaBean("get", sf.name)
+ ") for an Id field - does not exist in IdClass ("
+ sc.identity.objectidJClass.getName() + ").");
return false;
}
// The getter is OK. Look for the setter.
String mn = toJavaBean("set", sf.name);
for (Method m : sc.identity.objectidJClass.getDeclaredMethods()) {
if (mn.equals(m.getName())) {
if (m.getParameterTypes().length != 1) {
continue;
}
if (! sf.type.equals(Type.getDescriptor(m.getParameterTypes()[0]))) {
continue;
}
if (m.getReturnType() != Void.TYPE) {
continue;
}
// The field is a valid primary key element with well-formed
// getter/setter.
sf.primaryKey = true;
return false;
}
}
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to find field setter (" + mn
+ ") for an Id field - does not exist in IdClass ("
+ sc.identity.objectidJClass.getName() + ").");
return false;
}
// There is no IdClass. Then it should be a unique Long identifier.
if (!sf.type.equals("Ljava/lang/Long;")) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to define field (" + sf.name
+ ") as an Id - should be 'java.lang.Long'.");
return false;
}
if (sc.identity != null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to define field (" + sf.name
+ ") as an Id - an Id already exist.");
return false;
}
sc.identity = new SpeedoIdentity();
sc.identity.objectidClass = null;
sf.primaryKey = true;
logger.log(BasicLevel.DEBUG, sc.getSourceDescShort()
+ ": define field (" + sf.name
+ ") as an Id - strategy = " + sc.identity.getStrategyName() + ".");
return true;
}
private void parseGeneratedValue(GeneratedValue a, SpeedoClass sc) {
if (a == null) {
sc.identity.strategy = SpeedoIdentity.USER_ID;
return;
}
switch (a.strategy()) {
case TABLE:
sc.identity.strategy = SpeedoIdentity.DATASTORE_OLONG;
break;
case SEQUENCE:
sc.identity.strategy = SpeedoIdentity.DATASTORE_SEQUENCE;
sc.identity.sequenceName = a.generator();
break;
case IDENTITY:
sc.identity.strategy = SpeedoIdentity.DATASTORE_AUTO_ASSIGN;
break;
case AUTO:
sc.identity.strategy = SpeedoIdentity.DATASTORE_OLONG;
break;
default:
sc.identity.strategy = SpeedoIdentity.USER_ID;
break;
}
}
/**
* Parse a Basic annotation to define complementary information associated
* with a Field.
*
* @param b The Basic annotation.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
* @param jb true if the field is accessed using the JavaBean model.
*/
private void parseBasic(Basic b, SpeedoClass sc, SpeedoField sf, boolean jb) {
if (b == null) {
return;
}
if (sf.relationType != SpeedoField.NO_BI_RELATION) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": an association has already been defined for field ("
+ sf.name + ") - cannot define a Basic mapping.");
return;
}
if (b.fetch() == FetchType.LAZY) {
if (jb) {
parseFetchType(b.fetch(), sc, sf);
}
}
if (! b.optional()) {
if (isValidType(sf.type, PRIMITIVETYPES)) {
sf.nullValue = SpeedoNullValue.NONE;
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": try to define not null constraint on field (" + sf.name
+ ") - cannot apply to primitive types.");
} else {
sf.nullValue = SpeedoNullValue.EXCEPTION;
}
} else {
sf.nullValue = SpeedoNullValue.NONE;
}
}
/**
* Parse a FetchType associated to a given field.
*
* @param ft The FetchType to parse.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
*/
void parseFetchType(FetchType ft, SpeedoClass sc, SpeedoField sf) {
//TODO: support of fetch type for field (LAZY or EAGER).
}
/**
* Parse a Lob annotation to define complementary information associated
* with a Field.
*
* @param l The Lob annotation.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
* @param jb true if the field is accessed using the JavaBean model.
*/
private void parseLob(Lob l, SpeedoClass sc, SpeedoField sf, boolean jb) {
if (l == null) {
return;
}
}
/**
* Parse a CascadeType annotation associated to a given field.
*
* @param a The CascadeType.
* @param sf The SpeedoField under construction.
*/
private void parseCascadeType(CascadeType aa[], SpeedoField sf) {
for (CascadeType a : aa) {
switch (a) {
case ALL:
sf.propagate |= SpeedoField.PROPAG_ALL;
return;
case PERSIST:
sf.propagate |= SpeedoField.PROPAG_PERSIST;
break;
case MERGE:
sf.propagate |= SpeedoField.PROPAG_MERGE;
break;
case REMOVE:
sf.propagate |= SpeedoField.PROPAG_REMOVE;
break;
case REFRESH:
sf.propagate |= SpeedoField.PROPAG_REFRESH;
break;
default:
break;
}
}
}
/**
* Parse a ManyToOne annotation to define information related to the
* association.
*
* @param a The ManyToOne annotation to be parsed.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
*/
private void parseManyToOne(ManyToOne a, SpeedoClass sc, SpeedoField sf) {
if (a == null) {
return;
}
if (a.targetEntity() != null) {
// Temporarily stores type name of target association entity into
// field type for verification purpose at type parsing.
sf.type = a.targetEntity().getName();
}
parseCascadeType(a.cascade(), sf);
if (a.fetch() == FetchType.LAZY) {
parseFetchType(a.fetch(), sc, sf);
}
if (! a.optional()) {
sf.nullValue = SpeedoNullValue.EXCEPTION;
} else {
sf.nullValue = SpeedoNullValue.NONE;
}
}
/**
* Parse a OneToOne annotation to define information related to the
* association.
*
* @param a The OneToOne annotation to be parsed.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
*/
private void parseOneToOne(OneToOne a, SpeedoClass sc, SpeedoField sf) {
if (a == null) {
return;
}
if (a.targetEntity() != null) {
// Temporarily stores type name of target association entity into
// field type for verification purpose at type parsing.
sf.type = a.targetEntity().getName();
}
parseCascadeType(a.cascade(), sf);
if (a.fetch() == FetchType.LAZY) {
parseFetchType(a.fetch(), sc, sf);
}
if (! a.optional()) {
sf.nullValue = SpeedoNullValue.EXCEPTION;
} else {
sf.nullValue = SpeedoNullValue.NONE;
}
if (! a.mappedBy().equals("")) {
sf.reverseField = a.mappedBy();
sf.mappedByReversefield = true;
}
}
/**
* Parse a OneToMany annotation to define information related to the
* association.
*
* @param a The OneToMany annotation to be parsed.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
*/
private void parseOneToMany(OneToMany a, SpeedoClass sc, SpeedoField sf) {
if (a == null) {
return;
}
if (a.targetEntity() != null) {
// Temporarily stores type name of target association entity into
// field type for verification purpose at type parsing.
sf.type = a.targetEntity().getName();
}
parseCascadeType(a.cascade(), sf);
if (a.fetch() == FetchType.LAZY) {
parseFetchType(a.fetch(), sc, sf);
}
sf.nullValue = SpeedoNullValue.NONE;
if (a.mappedBy().equals("")) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": OneToMany association must define the reverse field that specify the mapping for field ("
+ sf.name + ").");
return;
}
sf.reverseField = a.mappedBy();
sf.mappedByReversefield = true;
}
/**
* Parse a ManyToMany annotation to define information related to the
* association.
*
* @param a The ManyToMany annotation to be parsed.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
*/
private void parseManyToMany(ManyToMany a, SpeedoClass sc, SpeedoField sf) {
if (a == null) {
return;
}
if (a.targetEntity() != null) {
// Temporarily stores type name of target association entity into
// field type for verification purpose at type parsing.
sf.type = a.targetEntity().getName();
}
parseCascadeType(a.cascade(), sf);
if (a.fetch() == FetchType.LAZY) {
parseFetchType(a.fetch(), sc, sf);
}
sf.nullValue = SpeedoNullValue.NONE;
if (! a.mappedBy().equals("")) {
sf.reverseField = a.mappedBy();
sf.mappedByReversefield = true;
}
}
/**
* Parse a JoinTable annotation to define the mapping of a "m-n"
* association field.
*
* @param jt The JoinTable annotation to be parsed.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
*/
private void parseJoinTable(JoinTable jt, SpeedoClass sc, SpeedoField sf) {
if (sf.mappedByReversefield) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": mapping is supposed to be defined by the reverse field for field ("
+ sf.name + ") - mapping information ignored.");
return;
}
sf.join = new SpeedoJoin();
/*TODO sf.join.extTable = parseTable(jt.table(), sc, null);*/
sf.join.mainTable = sc.mainTable;
// Parse joinColumns from JoinTable (may be empty)
for (JoinColumn jc : jt.joinColumns()) {
SpeedoJoinColumn sjcol = new SpeedoJoinColumn();
sjcol.column = new SpeedoColumn();
if (jc.referencedColumnName().equals("")) {
if (jc.name().equals("")) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": must define at least columnName or referencedColumnName for m-n relationship field ("
+ sf.name + ") - mapping information ignored.");
continue;
}
sjcol.column.name = jc.name();
sjcol.column.targetColumn = sjcol.column.name;
} else {
sjcol.column.targetColumn = jc.referencedColumnName();
if (jc.name().equals("")) {
sjcol.column.name = sjcol.column.targetColumn;
} else {
sjcol.column.name = jc.name();
}
}
sjcol.targetColumn = sjcol.column.targetColumn;
sjcol.column.table = sf.join.mainTable;
sf.join.columns.add(sjcol);
}
// parse inverseJoinColumns from JoinTable
for (JoinColumn jc : jt.inverseJoinColumns()) {
SpeedoColumn scol = new SpeedoColumn();
if (jc.referencedColumnName().equals("")) {
if (jc.name().equals("")) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": must define at least columnName or referencedColumnName for m-n relationship field ("
+ sf.name + ") - mapping information ignored.");
continue;
}
scol.name = jc.name();
scol.targetColumn = scol.name;
} else {
scol.targetColumn = jc.referencedColumnName();
if (jc.name().equals("")) {
scol.name = scol.targetColumn;
} else {
scol.name = jc.name();
}
}
sf.addColumn(scol);
}
}
/**
* Parse an array of JoinColumn annotations to define the mapping of a
* reference or association field with a one or many cardinality.
*
* @param jcs The array of JoinColumn annotation to be parsed.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
*/
private void parseJoinColumns(JoinColumn[] jcs, SpeedoClass sc, SpeedoField sf) {
if (sf.mappedByReversefield) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": mapping is supposed to be defined by the reverse field for field ("
+ sf.name + ") - mapping information ignored.");
return;
}
if (sf.relationType == SpeedoField.MANY_REFERENCE) {
sf.join = new SpeedoJoin();
}
for (JoinColumn jc : jcs) {
SpeedoColumn scol = new SpeedoColumn();
scol.targetColumn = jc.referencedColumnName();
if (scol.targetColumn.equals("")) {
if (jcs.length > 1) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to define the join of a reference or association field ("
+ sf.name + ") without defining names of referenced columns.");
continue;
}
}
if (jc.name().equals("")) {
if (jcs.length > 1) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to define the join of a reference or association field ("
+ sf.name + ") without defining names of join columns.");
continue;
}
scol.name = sf.name + "_" + scol.targetColumn;
} else {
scol.name = jc.name();
}
if (! jc.table().equals("")) {
scol.table = sc.getExtTable(jc.table(), true);
} else {
scol.table = sc.mainTable;
}
if (sf.relationType == SpeedoField.MANY_REFERENCE) {
if (sf.join.mainTable == null) {
sf.join.mainTable = scol.table;
} else if (sf.join.mainTable != scol.table) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": cannot map the reference field ("
+ sf.name + ") to columns belonging to several tables.");
continue;
}
}
SpeedoColumn fscol = sc.getColumn(scol.name, false);
if ((fscol != null) && (fscol.table == scol.table)) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": cannot define a column twice for column named ("
+ scol.name + ").");
continue;
}
if (sf.relationType == SpeedoField.MANY_REFERENCE) {
sf.join.columns.add(scol);
} else {
sf.addColumn(scol);
}
}
}
/**
* Parse an EmbeddedId annotation to define the SpeedoIdentifier associated with a
* SpeedoClass.
*
* @param ida The Id annotation to be parsed.
* @param sc The SpeedoClass under construction.
* @param sf The SpeedoField under construction.
*/
private void parseEmbeddedId(EmbeddedId ida, SpeedoClass sc, SpeedoField sf) {
if (ida == null) {
return;
}
if (sc.identity != null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to define field (" + sf.name
+ ") as an EmbeededId - an Id already exist.");
return;
}
try {
sc.identity = new SpeedoIdentity();
sc.identity.objectidClass = sf.getClassName();
sc.identity.objectidJClass = Class.forName(
Type.getType(sf.type).getClassName(), false, annotCL);
sc.identity.strategy = SpeedoIdentity.USER_ID;
if (parsePkClass(sc)) {
logger.log(BasicLevel.DEBUG, sc.getSourceDescShort()
+ ": define field (" + sf.name
+ ") as an EmbeddedId - Id class= '"
+ sc.identity.objectidClass + "'.");
}
} catch (ClassNotFoundException e) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to define field (" + sf.name
+ ") as an EmbeddedId - Id class not found("
+ Type.getType(sf.type).getClassName() + ").");
return;
}
}
/**
* Parse the unique constraints definition for a particular table.
* @param ucs The constraints to be parsed.
* @param sc The SpeedoClass under construction.
* @param t The SpeedoTable to which the constraints should be associated.
*/
private void parseUniqueConstraints(UniqueConstraint[]ucs, SpeedoClass sc,
SpeedoTable t) {
for (int i = 0; i < ucs.length; i++) { // iterate over constraints
ArrayList cols = new ArrayList();
int j;
for (j = 0; j < ucs[i].columnNames().length; j++) {
// Look for the column in those defined within the SpeedoClass
SpeedoColumn c = searchColumn(sc, ucs[i].columnNames()[j], t);
// Did not find the column
if (c == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to associate a unique constraint with a column ("
+ ucs[i].columnNames()[j]
+ ") that does not exist (ignored).");
break;
}
// Insert (sorted) the column into the list specifying the constraint
Iterator itc = cols.iterator();
int pos = 0;
while (itc.hasNext()) {
SpeedoColumn ct = (SpeedoColumn) itc.next();
if (c == ct) { // already present: ignored
pos = -1;
break;
}
if (c.name.compareTo(ct.name) > 0) {
pos++;
continue;
}
break;
}
if (pos != -1) {
cols.add(pos, c);
}
}
if (cols.size() == 0) { // empty constraint: ignore
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": table (" + t.name
+ ") has an empty unique constraint (ignored).");
continue;
}
if (j == ucs[i].columnNames().length) {
// The list of columns is consistent: create the constraint.
if (!t.addUniqueConstraint(cols)) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": unique constraint "
+ cols
+ " already defined (ignored).");
} else {
logger.log(BasicLevel.DEBUG, sc.getSourceDescShort()
+ ": add a unique constraint "
+ cols
+ " to table (" + t.name + ").");
}
}
}
}
/**
* Parse a table definition.
*
* @param t The Table annotation to be parsed.
* @param sc The SpeedoClass under construction.
* @param st The SpeedoTable under construction.
*/
private SpeedoTable parseTable(Table t, SpeedoClass sc, SpeedoTable st) {
if (st == null) {
st = new SpeedoTable();
}
if (!t.name().equals("")) {
st.name = t.name();
}
if (!t.catalog().equals("")) {
st.catalog = t.catalog();
}
if (!t.schema().equals("")) {
st.schema = t.schema();
}
parseUniqueConstraints(t.uniqueConstraints(), sc, st);
return st;
}
/**
* Parse a secondary table definition.
*
* @param st The SecondaryTable annotation definition.
* @param sc The SpeedoClass under construction.
*/
private void parseSecondaryTable(SecondaryTable st, SpeedoClass sc) {
for (SpeedoJoin sj : sc.joinToExtTables) {
if (!sj.extTable.name.equals(st.name())) {
continue;
}
if (!st.catalog().equals("")) {
sj.extTable.catalog = st.catalog();
}
if (!st.schema().equals("")) {
sj.extTable.schema = st.schema();
}
for (PrimaryKeyJoinColumn jc : st.pkJoinColumns()) {
SpeedoJoinColumn sjc = new SpeedoJoinColumn();
// Parse the colum name within the secondary table under definition
if (jc.name().equals("")) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to define the secondary table ("
+ st.name()
+ ") without defining names of FK join columns.");
continue;
} else {
sjc.column = searchColumn(sc, jc.name(), sj.extTable);
if (sjc.column == null) {
// Unknown column: define a new one
sjc.column = new SpeedoColumn();
sjc.column.name = jc.name();
sjc.column.sqlType = jc.columnDefinition();
sjc.column.table = sj.extTable;
}
}
// Parse the target column within the main table
sjc.targetColumn = jc.referencedColumnName();
if (sjc.targetColumn.equals("")) {
if (st.pkJoinColumns().length > 1) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": try to define a secondary table ("
+ st.name()
+ ") without defining names of referenced columns in main table.");
continue;
}
SpeedoField sf;
try {
sf = sc.getUniquePKField();
} catch (SpeedoException e) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": cannot find a unique PK column to define referenced column for secondary table ("
+ st.name() + ").");
continue;
}
if (sf.columns.length != 1) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": cannot find a unique PK column to define referenced column for secondary table ("
+ st.name() + ").");
continue;
}
sjc.targetColumn = sf.columns[0].name;
} else {
if (sc.getColumn(sjc.targetColumn, true) == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": referenced column (" + sjc.targetColumn
+ ") is not a column of the main table for secondary table ("
+ st.name() + ").");
continue;
}
}
sj.columns.add(sjc);
}
logger.log(BasicLevel.DEBUG, sc.getSourceDescShort()
+ ": secondary table (" + sj.extTable.name
+ ") - define join columns [" + sj.columns + "].");
parseUniqueConstraints(st.uniqueConstraints(), sc, sj.extTable);
return;
}
logger.log(BasicLevel.WARN, sc.getSourceDescShort()
+ ": try to define a secondary table ("
+ st.name()
+ ") that is unused for the mapping of this class - ignored.");
}
/**
*
*
* @param ao
* @param aos
* @param sc
*/
private void parseAttributeOverriding(
AttributeOverride ao,
AttributeOverrides aos,
SpeedoClass sc) {
if ((aos == null) && (ao == null)) {
return;
}
if (sc.inheritance == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": cannot override attribute if no inheritance defined for this class - overriding ignored");
return;
}
AttributeOverride[] target; // The array of AttributeOverride to be annalysed
if (aos == null) {
target = new AttributeOverride[] {ao};
} else {
if (ao != null) {
target = new AttributeOverride[aos.value().length + 1];
System.arraycopy(aos.value(), 0, target, aos.value().length, 0);
target[aos.value().length] = ao;
} else {
target = aos.value();
}
}
// target is completed; perform analysis
for (AttributeOverride a : target) {
// TODO:
// Look for the field in inherited classes
SpeedoField inhsf = sc.getInheritedField(a.name());
if (inhsf == null) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort()
+ ": overriden attribute does not exist in inherited class - overriding ignored");
continue;
}
SpeedoInheritedField sif = sc.inheritance.newSpeedoInheritedField(inhsf);
parseOverrideColumn(a.column(), sc, sif);
}
}
/**
*
*
* @param ao
* @param aos
* @param sc
* @param sf
*/
private void parseEmbeddedOverriding(
AttributeOverride ao,
AttributeOverrides aos,
SpeedoClass sc,
SpeedoField sf) {
// TODO:
}
public static final String[] CBNAMES = {
"PrePersist",
"PostPersist",
"PreRemove",
"PostRemove",
"PreUpdate",
"PostUpdate",
"PostLoad"
};
public static final int[] CBIDS = {
HomeItf.PRE_NEW,
HomeItf.POST_CREATE,
HomeItf.PRE_REMOVE,
HomeItf.POST_DELETE,
HomeItf.PRE_UPDATE,
HomeItf.POST_UPDATE,
HomeItf.POST_LOAD
};
public static final Class[] CBCLASSES = {
PrePersist.class,
PostPersist.class,
PreRemove.class,
PostRemove.class,
PreUpdate.class,
PostUpdate.class,
PostLoad.class
};
/**
* Parse the callbacks defined either within the class or within an external
* listener class.
*
* @param meths The methods from which to look for callbacks.
* @param sc The SpeedoClass under construction.
* @param listener The listener class if the callback belongs to such a class.
*/
private void parseCallBacks(Method[] meths, SpeedoClass sc, Class listener) {
// Look for callback methods
SpeedoCallback scb = null;
for (Method m : meths) {
int i = 0;
for (Class annotc : CBCLASSES) {
if (m.getAnnotation(annotc) == null) {
i++;
continue;
}
ArrayList cbl = (ArrayList) sc.callBacks.get(CBIDS[i]);
if (scb == null) {
scb = new SpeedoCallback();
}
if (cbl != null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort() + ": "
+ CBNAMES[i] + " callback - "
+ "already defined.");
} else {
cbl = new ArrayList();
sc.callBacks.put(CBIDS[i], cbl);
}
scb.methodByteCodeSignature = isWellFormedCallback(m, listener, sc, CBNAMES[i]);
if (scb.methodByteCodeSignature == null) {
continue;
}
logger.log(BasicLevel.DEBUG, sc.getSourceDescShort()
+ ": new callback defined (" + CBNAMES[i] + ", "
+ ((listener == null) ? "" : listener.getName())
+ m.getName() + ")");
scb.callbackName = m.getName();
scb.callbackType = CBIDS[i];
scb.listenerClassName = (listener != null) ? listener.getName() : null;
cbl.add(scb);
scb = null;
}
}
}
/**
* Search a column within those associated to this class.
*
* @param sc The SpeedoClass under construction.
* @param cname The name of the column to be searched.
* @param t The SpeedoTable to which it should belong.
* @return The column found or null if none.
*/
private SpeedoColumn searchColumn(SpeedoClass sc, String cn, SpeedoTable t) {
SpeedoColumn c = null;
Iterator itf = sc.fields.values().iterator();
while (itf.hasNext()) {
SpeedoField ft = (SpeedoField) itf.next();
for (int k = 0; k < ft.columns.length; k++) {
if (ft.columns[k].table != t) {
continue;
}
if (ft.columns[k].name.equals(cn)) {
c = ft.columns[k];
break;
}
}
}
return c;
}
/**
* Verify if the callback method is well formed. If this is a callback from
* the class (prefix is ""), it should be "void CALLBACK()". If it is a
* callback from the listener class, it should be "void CALLBACK(Object)".
*
* @param m The method associated to the callback event.
* @param listener The listener class if the callback belongs to such a class.
* @param sc The SpeedoClass under construction.
* @param cbe The callback event under definition.
* @return Null if malformed. Otherwise, it defines the byte code
* signature of the callback method.
*/
private String isWellFormedCallback(Method m, Class listener,
SpeedoClass sc, String cbe) {
String res = "(";
if (m.getReturnType() != Void.TYPE) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": " + cbe
+ " callback malformed - return type should be void.");
return null;
}
if (listener == null) {
if (m.getParameterTypes().length != 0) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": " + cbe
+ " class callback malformed - should not have parameters.");
return null;
}
} else {
if (m.getParameterTypes().length != 1) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": " + cbe
+ " listener class callback malformed - should have a unique parameter.");
return null;
}
Class scc;
try {
scc = (Class) Class.forName(sc.getFQName(), false, annotCL);
} catch (ClassNotFoundException e) {
// Sure to find it there !!!! NPE otherwise...
scc = null;
}
if (! m.getParameterTypes()[0].isAssignableFrom(scc)) {
nbErrors++;
logger.log(BasicLevel.ERROR, sc.getSourceDescShort() + ": " + cbe
+ " listener class callback malformed - should have a parameter compliant with type ("
+ scc.getName() + "), found ("
+ m.getParameterTypes()[0].getName() + ").");
return null;
}
res += Type.getDescriptor(m.getParameterTypes()[0]);
}
return res + ")V";
}
private String toJavaBean(String prefix, String fieldname) {
prefix += Character.toUpperCase(fieldname.charAt(0));
prefix += fieldname.substring(1, fieldname.length());
return prefix;
}
/**
* Look if the given type belongs to a set of valid ones.
*
* @param type The typeto be verified.
* @param validtypes The set of valid types.
* @return true if it belongs to the given set of types.
*/
private boolean isValidType(String type, String[]validtypes) {
for (String t : validtypes) {
if (t.equals(type)) {
return true;
}
}
return false;
}
/**
* Log a warning for each defined annotations in the annots array.
*
* @param annots The array of annotation that shoudl not be used at this place.
* @param sc The SpeedoClass under construction.
* @param fieldname The name of the field with which these annotations are associated.
* @param kind The kind of field under parsing.
*/
private void excludeAnnotation(Object[] annots, SpeedoClass sc, String fieldname, String kind) {
for (Object annot : annots) {
if (annot != null) {
logger.log(BasicLevel.WARN, sc.getSourceDescShort() + ": annotation ("
+ annot.getClass().getName() + ") invalid for "
+ kind + " (" + fieldname + ") - ignored.");
}
}
}
}
/**
* This ClassLoader is used to load classes that we want to extract the
* annotations from.
*
* @author P. Dechamboux
*
*/
class LoaderForAnnotAccess extends URLClassLoader {
Logger logger;
LoaderForAnnotAccess(Logger log, ClassLoader pcl) {
super(new URL[0], pcl);
logger = log;
}
void addDirURL(String dir) throws SpeedoException {
try {
String furl = "file://" + dir + "/";
logger.log(BasicLevel.DEBUG,
"Adding URL to compiler class loader: " + furl);
addURL(new URL(furl));
} catch (MalformedURLException e) {
throw new SpeedoXMLError(e);
}
}
}