/**
* 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.generation.parser.jdo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.generation.api.SpeedoXMLError;
import org.objectweb.speedo.generation.parser.AbstractParser;
import org.objectweb.speedo.lib.Personality;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.metadata.SpeedoCollection;
import org.objectweb.speedo.metadata.SpeedoColumn;
import org.objectweb.speedo.metadata.SpeedoCommonField;
import org.objectweb.speedo.metadata.SpeedoDiscriminator;
import org.objectweb.speedo.metadata.SpeedoElement;
import org.objectweb.speedo.metadata.SpeedoExtension;
import org.objectweb.speedo.metadata.SpeedoFetchGroup;
import org.objectweb.speedo.metadata.SpeedoField;
import org.objectweb.speedo.metadata.SpeedoIdentity;
import org.objectweb.speedo.metadata.SpeedoIndex;
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.SpeedoPredefinedQuery;
import org.objectweb.speedo.metadata.SpeedoTable;
import org.objectweb.speedo.metadata.SpeedoVersion;
import org.objectweb.speedo.metadata.SpeedoXMLDescriptor;
import org.objectweb.speedo.sequence.jdo.JDOSequence;
import org.objectweb.speedo.sequence.lib.SpeedoSequence;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* This class is a parser of the JDO2 persistent descriptor. It includes the
* parsing the O/R mapping.
*
* @author S.Chassande-Barrioz
*/
public class JDO2Parser extends AbstractParser {
public JDO2Parser() {
super(Personality.JDO);
}
public void process() throws SpeedoException {
super.process();
//compute fields number for each class
for (Iterator itDesc = scp.getXmldescriptor().values().iterator(); itDesc.hasNext();) {
SpeedoXMLDescriptor desc = (SpeedoXMLDescriptor) itDesc.next();
for (Iterator itPack = desc.packages.values().iterator(); itPack.hasNext();) {
SpeedoPackage sp = (SpeedoPackage) itPack.next();
for (Iterator itclass = sp.classes.values().iterator(); itclass.hasNext();) {
SpeedoClass clazz = (SpeedoClass) itclass.next();
clazz.computeFieldNumbers();
}
}
}
}
protected String getLoggerName() {
return "jdo2";
}
protected Object treatDocument(Node node, Object mo) throws SpeedoException {
SpeedoXMLDescriptor xmlDesc = (SpeedoXMLDescriptor) mo;
Map docChildren = groupChildrenByName(node);
List l = (List) docChildren.remove("jdo");
if (l != null) {
for (int i = 0; i < l.size(); i++) {
treatJdoNode((Node) l.get(i), xmlDesc);
}
}
return mo;
}
protected Object treatJdoNode(Node jdoNode, SpeedoXMLDescriptor xmlDesc) throws SpeedoException {
Map jdoChildren = groupChildrenByName(jdoNode);
List packages = (List) jdoChildren.remove("package");
if (packages != null) {
for (int i = 0; i < packages.size(); i++) {
Node packageNode = (Node) packages.get(i);
Node n = packageNode.getAttributes().getNamedItem("name");
if (n == null) {
logger.log(BasicLevel.ERROR, "Attribute 'name' for tag package required.");
continue;
}
SpeedoPackage p = new SpeedoPackage();
p.name = n.getNodeValue();
xmlDesc.add(p, true, logger);
logger.log(BasicLevel.DEBUG, "Parsing package: name= " + p.name);
Map packageChildren = groupChildrenByName(packageNode);
treatExtensions((List) packageChildren.remove("extension"), p);
List l = (List) packageChildren.remove("sequence");
if (l != null) {
for (int j = 0; j < l.size(); j++) {
Node classNode = (Node) l.get(j);
treatSequenceNode(classNode, p);
}
}
l = (List) packageChildren.remove("class");
if (l != null) {
for (int j = 0; j < l.size(); j++) {
Node classNode = (Node) l.get(j);
treatClassNode(classNode, p);
}
}
}
}
List queries = (List) jdoChildren.remove("query");
if (queries != null) {
xmlDesc.queries = new ArrayList(queries.size());
for (int i = 0; i < queries.size(); i++) {
SpeedoPredefinedQuery spq = getQuery((Node) queries.get(i));
xmlDesc.queries.add(spq);
}
}
return xmlDesc;
}
private Map groupChildrenByName(Node n) {
NodeList nl = n.getChildNodes();
int size = nl.getLength();
if (size == 0) {
return Collections.EMPTY_MAP;
}
HashMap result = new HashMap(size);
for (int i = 0; i < size; i++) {
Node child = nl.item(i);
String name = child.getNodeName();
List l = (List) result.get(name);
if (l == null) {
l = new ArrayList();
result.put(name, l);
}
l.add(child);
}
return result;
}
private void unmanaged(Node node, Map unmanagedChildren) {
if (unmanagedChildren!= null && unmanagedChildren.size() > 0) {
logger.log(BasicLevel.WARN, "Umanaged sub node of "
+ node.getNodeName() + ": " + unmanagedChildren.keySet());
}
}
private void treatSequenceNode(Node seqNode, SpeedoPackage p) throws SpeedoException {
SpeedoSequence s = new JDOSequence();
Node n = null;
//<!ATTLIST sequence name CDATA #REQUIRED>
n = seqNode.getAttributes().getNamedItem("name");
if (n == null)
throw new SpeedoXMLError("Attribute name for tag sequence requested.");
s.name = n.getNodeValue();
p.addSequence(s);
//<!ATTLIST sequence datastore-sequence CDATA #IMPLIED>
n = seqNode.getAttributes().getNamedItem("datastore-sequence");
if (n != null)
s.datastoreName = n.getNodeValue();
//<!ATTLIST sequence factory-class CDATA #IMPLIED>
n = seqNode.getAttributes().getNamedItem("factory-class");
if (n != null) {
s.factoryClass = n.getNodeValue();
}
//<!ATTLIST sequence strategy (nontransactional|contiguous|noncontiguous) #REQUIRED>
n = seqNode.getAttributes().getNamedItem("strategy");
if (n == null) {
throw new SpeedoXMLError("Attribute strategy for tag sequence requested.");
}
s.strategy = SpeedoSequence.strategyToByte(n.getNodeValue());
//<!ELEMENT sequence (extension*)>
Map seqChildren = groupChildrenByName(seqNode);
treatExtensions((List) seqChildren.get("extension"), p);
if (debug) {
logger.log(BasicLevel.DEBUG, "New sequence: "
+ "name=" + s.name
+ " / datastoreName = " + s.datastoreName
+ " / factoryClass = " + s.factoryClass
+ " / strategy = " + s.strategy
);
}
}
private void treatIndexNode(Node indexNode, SpeedoClass sc) throws SpeedoException {
SpeedoIndex si = new SpeedoIndex();
Node n = null;
//attribut name (compulsory)
n = indexNode.getAttributes().getNamedItem("name");
if (n == null)
throw new SpeedoXMLError("Attribute name for tag index requested.");
si.name = n.getNodeValue();
//attribute unique
n = indexNode.getAttributes().getNamedItem("unique");
if (n != null)
si.unique = Boolean.valueOf(n.getNodeValue()).booleanValue();
//attribute table (compulsory)
n = indexNode.getAttributes().getNamedItem("table");
if (n == null)
throw new SpeedoXMLError("Attribute table for tag index requested.");
si.table = n.getNodeValue();
//add the SpeedoIndex to the SpeedoTable
boolean linked = false;
//test the main table
if (sc.mainTable != null) {
if (sc.mainTable.name.equals(si.table)) {
sc.mainTable.indexes.add(si);
linked = true;
}
}
//and if no success with the main table try the joined tables
if (!linked && sc.joinToExtTables != null) {
for (int i = 0; i < sc.joinToExtTables.length && !linked; i++) {
if (sc.joinToExtTables[i].extTable.name.equals(si.name)) {
sc.joinToExtTables[i].extTable.indexes.add(si);
linked = true;
}
}
}
Map classChildren = groupChildrenByName(indexNode);
//extension*, (column|field|property)*
treatExtensions((List) classChildren.get("extension"), si);
List l = (List) classChildren.get("field");
if (l != null) {
for (int j = 0; j < l.size(); j++) {
treatFieldNode((Node) l.get(j), sc, si);
}
}
si.speedoClass = sc;
// TODO: unmanaged stuff of class
unmanaged(null, new String[]{"column", "property"}, indexNode, classChildren);
}
private void treatClassNode(Node classNode, SpeedoPackage p) throws SpeedoException {
Node n = null;
//attribute name (compulsory)
n = classNode.getAttributes().getNamedItem("name");
if (n == null) {
logger.log(BasicLevel.ERROR, "Attribute 'name' for tag class required.");
return;
}
SpeedoClass c = new SpeedoClass();
c.name = n.getNodeValue();
p.addClass(c, true, logger);
//attribute identity-type
n = classNode.getAttributes().getNamedItem("identity-type");
if (n != null)
c.setIdentityType(SpeedoIdentity.getStrategy(n.getNodeValue()));
//attribute object-id
n = classNode.getAttributes().getNamedItem("objectid-class");
if (n != null) {
c.identity.objectidClass = n.getNodeValue();
c.setIdentityType(SpeedoIdentity.USER_ID);
}
//attribute requires-extent is not taken into account
//attribute detachable
n = classNode.getAttributes().getNamedItem("detachable");
if (n != null)
c.isDetachable = Boolean.valueOf(n.getNodeValue()).booleanValue();
//attribute persistence-capable-superclass
n = classNode.getAttributes().getNamedItem("persistence-capable-superclass");
if (n != null) {
if (c.inheritance == null) {
c.inheritance = new SpeedoInheritance(c);
}
c.inheritance.superClassName = n.getNodeValue();
}
//attribute table
n = classNode.getAttributes().getNamedItem("table");
if (n != null) {
c.mainTable = new SpeedoTable();
c.mainTable.name = n.getNodeValue();
}
if (c.mainTable == null) {
if (c.inheritance == null
|| c.inheritance.strategy == SpeedoInheritance.STRATEGY_NEW_TABLE) {
c.mainTable = new SpeedoTable();
c.mainTable.name = c.name.toUpperCase();
}
}
logger.log(BasicLevel.DEBUG, "New class: "
+ "name=" + c.name
+ " / id = " + c.getIdentityType()
+ " / idClass = " + c.identity.objectidClass
+ " / detachable = " + c.isDetachable
);
Map classChildren = groupChildrenByName(classNode);
//extension*, implements*, datastore-identity?, inheritance?, version?,
// join*, foreign-key*, index*, unique*, field*, query*, fetch-group*, extension*
treatExtensions((List) classChildren.get("extension"), c);
List l = (List) classChildren.get("datastore-identity");
if (l != null) {
treatDataStoreId((Node) l.get(0), c);
}
l = (List) classChildren.get("inheritance");
if (l != null) {
treatInheritance((Node) l.get(0), c);
}
l = (List) classChildren.get("version");
if (l != null) {
treatVersion((Node) l.get(0), c);
}
l = (List) classChildren.get("join");
if (l != null) {
for (int j = 0; j < l.size(); j++) {
Object[] os = getJoin((Node) l.get(j), null);
SpeedoJoin join = (SpeedoJoin) os[0];
String tableName = (String) os[1];
//define an external/secondary table
c.addJoin(join);
if (c.mainTable != null) {
join.mainTable = c.mainTable;
}
if (tableName != null) {
join.extTable = new SpeedoTable();
join.extTable.name = tableName;
join.extTable.join = join;
}
}
}
l = (List) classChildren.get("field");
if (l != null) {
for (int j = 0; j < l.size(); j++) {
treatFieldNode((Node) l.get(j), c);
}
}
l = (List) classChildren.get("query");
if (l != null) {
for (int j = 0; j < l.size(); j++) {
SpeedoPredefinedQuery spq = getQuery((Node) l.get(j));
c.name2query.put(spq.name, spq);
}
}
l = (List) classChildren.get("index");
if (l != null) {
for (int j = 0; j < l.size(); j++) {
treatIndexNode((Node) l.get(j), c);
}
}
l = (List) classChildren.get("fetch-group");
if (l != null) {
for (int j = 0; j < l.size(); j++) {
treatFetchGroupNode((Node) l.get(j), c);
}
}
//TODO: unmanaged stuff of class
unmanaged(null, new String[]{"foreign-key", "unique"},
classNode, classChildren);
}
private void treatDataStoreId(Node dsiNode, SpeedoClass moClass) throws SpeedoException {
SpeedoIdentity ident = new SpeedoIdentity(); //default strategy is native
//<!ATTLIST datastore-identity strategy CDATA 'native'>
Node n = dsiNode.getAttributes().getNamedItem("strategy");
if (n == null)
throw new SpeedoXMLError("Attribute strategy for tag datastore-identity requested.");
ident.strategy = SpeedoIdentity.getStrategy(n.getNodeValue());
//<!ATTLIST datastore-identity sequence CDATA #IMPLIED>
n = dsiNode.getAttributes().getNamedItem("sequence");
if (n != null)
ident.sequenceName = n.getNodeValue();
//<!ATTLIST datastore-identity column CDATA #IMPLIED>
List columns = null;
n = dsiNode.getAttributes().getNamedItem("column");
if (n != null) {
columns = new ArrayList();
columns.add(new SpeedoColumn(n.getNodeValue()));
}
//<!ELEMENT datastore-identity (extension*, column*, extension*)>
//inner element column
Map dsiChildren = groupChildrenByName(dsiNode);
treatExtensions((List) dsiChildren.get("extension"), ident);
//TODO: unmanaged stuff of class
unmanaged(null, new String[]{"index", "unique", "fetch-group"},
dsiNode, dsiChildren);
List columnNodes = (List) dsiChildren.get("column");
if (columnNodes != null) {
for (int i = 0; i < columnNodes.size(); i++) {
if (columns == null) {
columns = new ArrayList();
}
Node columnNode = (Node) columnNodes.get(i);
columns.add(getColumn(columnNode));
}
}
if (columns != null) {
moClass.identity.setColumns(columns);
}
//bind the identity to the moClass
moClass.identity = ident;
if (debug) {
logger.log(BasicLevel.DEBUG, "New datastore-identity: "
+ "identity=" + ident);
}
}
private void treatInheritance(Node inhNode, SpeedoClass moClass) throws SpeedoException {
if (moClass.inheritance == null) {
moClass.inheritance = new SpeedoInheritance(moClass);
}
//<!ATTLIST inheritance strategy CDATA #IMPLIED>
Node n = inhNode.getAttributes().getNamedItem("strategy");
if (n != null) {
moClass.inheritance.strategy =
SpeedoInheritance.parseStrategy(n.getNodeValue());
logger.log(BasicLevel.DEBUG, "defines the strategy '"
+ SpeedoInheritance.strategy2str(moClass.inheritance.strategy));
}
//<!ELEMENT inheritance (extension*, join?, discriminator?, extension*)>
Map inhChildren = groupChildrenByName(inhNode);
//inner element extension
treatExtensions((List) inhChildren.get("extension"), moClass.inheritance);
//inner element join
List l = (List) inhChildren.get("join");
if (l != null) {
//define a join to the super class (vertical mapping)
Object[] os = getJoin((Node) l.get(0), null);
moClass.inheritance.join = (SpeedoJoin) os[1];
}
//inner element discriminator
l = (List) inhChildren.get("discriminator");
if (l != null) {
treatDiscriminator((Node) l.get(0), moClass.inheritance);
}
}
private void treatDiscriminator(Node discNode, SpeedoInheritance inh)throws SpeedoException {
if (inh.discriminator == null) {
inh.discriminator = new SpeedoDiscriminator();
}
//<!ATTLIST discriminator strategy CDATA #IMPLIED>
Node n = discNode.getAttributes().getNamedItem("strategy");
if (n != null) {
inh.discriminator.strategy =
SpeedoDiscriminator.parseStrategy(n.getNodeValue());
}
//<!ELEMENT discriminator (extension*, column*, index?, extension*)>
//inner element extension
Map discChildren = groupChildrenByName(discNode);
//<!ATTLIST discriminator value CDATA #IMPLIED>
n = discNode.getAttributes().getNamedItem("value");
String val = null;
if (n != null) {
val = n.getNodeValue();
}
//<!ATTLIST discriminator column CDATA #IMPLIED>
n = discNode.getAttributes().getNamedItem("column");
SpeedoColumn col = null;
if (n != null) {
col = new SpeedoColumn(n.getNodeValue());
} else {
//inner element column
List columnNodes = (List) discChildren.get("column");
if (columnNodes != null && columnNodes.size() == 1) {
Node columnNode = (Node) columnNodes.get(0);
col = getColumn(columnNode);
}
}
if (col == null) {
if (val != null) {
//try to use the default column
}
} else {
if (val != null) {
} else {
//column definition on abstract class
}
}
SpeedoNoFieldColumn snfc = new SpeedoNoFieldColumn();
snfc.column = col;
inh.discriminator.elements.add(snfc);
if (val != null) {
if (inh.discriminatorValues == null) {
inh.discriminatorValues = new HashMap();
}
inh.discriminator.setDiscriminatorValue(val, inh, snfc);
}
treatExtensions((List) discChildren.get("extension"),
inh.discriminator);
unmanaged(new String[]{"indexed"}, new String[]{"index"},
discNode, discChildren);
}
private Object[] getJoin(Node joinNode, SpeedoJoin j) throws SpeedoException {
Node n = joinNode.getAttributes().getNamedItem("table");
if (j == null) {
j = new SpeedoJoin();
}
n = joinNode.getAttributes().getNamedItem("delete-action");
if (n != null) {
String v = n.getNodeValue();
if ("restrict".equals(v)) {
j.deleteAction = SpeedoJoin.ACTION_RESTRICT;
} else if ("cascade".equals(v)) {
j.deleteAction = SpeedoJoin.ACTION_CASCADE;
} else if ("null".equals(v)) {
j.deleteAction = SpeedoJoin.ACTION_NULL;
} else if ("none".equals(v)) {
j.deleteAction = SpeedoJoin.ACTION_NONE;
} else if ("default".equals(v)) {
j.deleteAction = SpeedoJoin.ACTION_DEFAULT;
} else {
j.deleteAction = SpeedoJoin.ACTION_DEFAULT;
}
}
j.setUnique(getBooleanAttributeValue(joinNode, "unique", j.getUnique()));
j.setIndexed(getBooleanAttributeValue(joinNode, "indexed", j.getIndexed()));
j.setOuter(getBooleanAttributeValue(joinNode, "indexed", j.getOuter()));
n = joinNode.getAttributes().getNamedItem("column");
if (n != null) {
SpeedoJoinColumn sjc = new SpeedoJoinColumn(new SpeedoColumn(n.getNodeValue()));
j.columns.add(sjc);
}
Map joinChildren = groupChildrenByName(joinNode);
List l = (List) joinChildren.get("column");
if (l != null) {
for (int i = 0; i < l.size(); i++) {
j.columns.add(new SpeedoJoinColumn(getColumn((Node) l.get(i))));
}
}
n = joinNode.getAttributes().getNamedItem("table");
String tableName = null;
if (n != null) {
tableName = n.getNodeValue();
}
return new Object[]{j, tableName};
}
private void treatVersion(Node versionNode, SpeedoClass moClass) throws SpeedoException {
SpeedoVersion v = new SpeedoVersion();
//attribute embedded-element
//<!ATTLIST version strategy CDATA #IMPLIED>
Node n = versionNode.getAttributes().getNamedItem("strategy");
v.strategy = SpeedoVersion.toByte(n.getNodeValue());
//<!ATTLIST version indexed (true|false|unique) #IMPLIED>
n = versionNode.getAttributes().getNamedItem("strategy");
if (n != null) {
//TODO: Speedo does not support yet the indexed attribute in version tag
logger.log(BasicLevel.WARN, "Speedo does not support yet the indexed attribute in version tag");
}
//<!ATTLIST version column CDATA #IMPLIED>
List columns = null;
n = versionNode.getAttributes().getNamedItem("column");
if (n != null) {
columns = new ArrayList();
columns.add(new SpeedoColumn(n.getNodeValue()));
}
//<!ELEMENT version (extension*, column*, index?, extension*)>
Map versionChildren = groupChildrenByName(versionNode);
unmanaged(new String[]{"column"}, new String[]{"column", "index"},
versionNode, versionChildren);
moClass.version = v;
logger.log(BasicLevel.DEBUG, "New version: strategy=" + v.strategy);
}
private void treatExtensions(List nodes, SpeedoElement se) throws SpeedoException {
if (nodes != null) {
for (int i = 0; i < nodes.size(); i++) {
treatExtension((Node) nodes.get(i), se);
}
}
}
private void treatExtension(Node node, SpeedoElement se) throws SpeedoException {
SpeedoExtension e = new SpeedoExtension();
//<!ATTLIST extension vendor-name CDATA #REQUIRED>
Node n = node.getAttributes().getNamedItem("vendor-name");
e.vendorName = n.getNodeValue();
//<!ATTLIST extension key CDATA #IMPLIED>
n = node.getAttributes().getNamedItem("key");
if (n != null) {
e.key = n.getNodeValue();
}
//<!ATTLIST extension value CDATA #IMPLIED>
n = node.getAttributes().getNamedItem("value");
if (n != null) {
e.value = n.getNodeValue();
}
//<!ELEMENT extension ANY>
logger.log(BasicLevel.DEBUG, "Parse extension name=" + e.key + ", value=" + e.value);
se.addExtension(e);
e.owner = se;
}
private SpeedoColumn getColumn(Node node) throws SpeedoException {
SpeedoColumn c = new SpeedoColumn();
Node n = node.getAttributes().getNamedItem("name");
if (n != null) {
c.name = n.getNodeValue();
}
n = node.getAttributes().getNamedItem("target");
if (n != null) {
c.targetColumn = n.getNodeValue();
}
n = node.getAttributes().getNamedItem("target-field");
if (n != null) {
c.targetField = n.getNodeValue();
}
n = node.getAttributes().getNamedItem("jdbc-type");
if (n != null) {
c.jdbcType = n.getNodeValue();
}
n = node.getAttributes().getNamedItem("sql-type");
if (n != null) {
c.sqlType = n.getNodeValue();
}
n = node.getAttributes().getNamedItem("length");
if (n != null) {
c.length = Integer.parseInt(n.getNodeValue());
}
n = node.getAttributes().getNamedItem("scale");
if (n != null) {
c.scale = Integer.parseInt(n.getNodeValue());
}
c.allowNull = getBooleanAttributeValue(node, "allows-null", c.allowNull);
n = node.getAttributes().getNamedItem("default-value");
if (n != null) {
c.defaultValue = n.getNodeValue();
}
return c;
}
private SpeedoPredefinedQuery getQuery(Node queryNode) throws SpeedoException {
//TODO: verify DTD about query
SpeedoPredefinedQuery spq = new SpeedoPredefinedQuery();
//<!ATTLIST query name CDATA #IMPLIED>
spq.name = getStringAttributeValue(queryNode, "name", null);
//<!ATTLIST query language CDATA #IMPLIED>
spq.language = getStringAttributeValue(queryNode, "language", null);
//<!ELEMENT query (#PCDATA|extension)*>
spq.query = queryNode.getNodeValue();
Map queryChildren = groupChildrenByName(queryNode);
treatExtensions((List) queryChildren.get("extension"), spq);
return spq;
}
private SpeedoFetchGroup getFetchGroup(Node fgNode, SpeedoClass fieldOwner) throws SpeedoException {
SpeedoFetchGroup fg = new SpeedoFetchGroup();
//<!ATTLIST fetch-group name CDATA #REQUIRED>
Node n = fgNode.getAttributes().getNamedItem("name");
fg.name = n.getNodeValue();
//<!ATTLIST fetch-group post-load (true|false) #IMPLIED>
n = fgNode.getAttributes().getNamedItem("post-load");
if (n != null){
fg.postLoad = Boolean.valueOf(n.getNodeValue()).booleanValue();
} else {
if (fg.name.equals("default")) {
fg.postLoad = true;
} else {
fg.postLoad = false;
}
}
Map fgChildren = groupChildrenByName(fgNode);
//<!ELEMENT fetch-group (fetch-group|field)*>
List l = (List) fgChildren.get("field");
if (l != null) {
for (int j = 0; j < l.size(); j++) {
Node fieldNode = (Node) l.get(j);
String fieldName = getStringAttributeValue(fieldNode, "name", null);
if (fieldName != null) {
SpeedoField field = fieldOwner.getField(fieldName);
if (field != null) {
fg.addField(field);
} else {
logger.log(BasicLevel.WARN, "Bad field name '"
+ fieldName + "'in the fetch group '"
+ fg.name + "' in the class "
+ fieldOwner.getSourceDesc() + ".");
}
}
}
}
l = (List) fgChildren.get("fetch-group");
if (l != null) {
for (int j = 0; j < l.size(); j++) {
SpeedoFetchGroup subfg = getFetchGroup((Node) l.get(j), fieldOwner);
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "add fetchgroup '"
+ subfg.name + "' into the fetchgroup '"
+ fg.name +"'");
}
fg.addFetchGroup(subfg);
}
}
return fg;
}
private void treatFieldNode(Node fieldNode, SpeedoClass moClass, SpeedoIndex moIndex) throws SpeedoException {
//at the moment, only support for field with name attribute
//no support for field definition within the index tag
Node n = null;
//<!ATTLIST field name CDATA #REQUIRED>
n = fieldNode.getAttributes().getNamedItem("name");
if (n == null)
throw new SpeedoXMLError("Attribute name for tag field requested.");
String name = n.getNodeValue();
//add the field name to the index
moIndex.fieldNames.add(name);
//try to add the column name(s) to the index
SpeedoField sf = (SpeedoField) moClass.fields.get(name);
if (sf == null)
throw new SpeedoXMLError("The field " + name + " must be defined for the class "
+ moClass.name +".");
if( sf.columns != null && sf.columns.length != 0) {
for(int i = 0; i < sf.columns.length; i++) {
moIndex.columnNames.add(sf.columns[i].name);
}
} else {
logger.log(BasicLevel.WARN, "The column(s) for the field " + name + " has not been defined yet." );
}
}
private void treatFieldNode(Node fieldNode, SpeedoClass moClass) throws SpeedoException {
Node n = null;
//<!ATTLIST field name CDATA #REQUIRED>
n = fieldNode.getAttributes().getNamedItem("name");
if (n == null)
throw new SpeedoXMLError("Attribute name for tag field requested.");
String name = n.getNodeValue();
int dotIdx = name.lastIndexOf(".");
SpeedoField f = null;
SpeedoCommonField cf;
FieldContext fc = new FieldContext();
fc.fieldNode = fieldNode;
if(dotIdx != -1){
cf = moClass.inheritance.newSpeedoInheritedField(name);
} else {
f = new SpeedoField();
cf = f;
f.name = n.getNodeValue();
f.moClass = moClass;
//<!ATTLIST field persistence-modifier (persistent|transactional|none) #IMPLIED>
n = fieldNode.getAttributes().getNamedItem("persistence-modifier");
if (n != null)
f.persistenceStatus = SpeedoField.parsePersistenceStatus(n.getNodeValue());
//<!ATTLIST field primary-key (true|false) 'false'>
n = fieldNode.getAttributes().getNamedItem("primary-key");
if (n != null)
f.primaryKey = Boolean.valueOf(n.getNodeValue()).booleanValue();
//<!ATTLIST field null-value (exception|default|none) 'none'>
n = fieldNode.getAttributes().getNamedItem("null-value");
if (n != null) {
f.nullValue = SpeedoNullValue.toByte(n.getNodeValue());
}
//<!ATTLIST field default-fetch-group (true|false) #IMPLIED>
n = fieldNode.getAttributes().getNamedItem("default-fetch-group");
if (n != null)
f.defaultFetchGroup = Boolean.valueOf(n.getNodeValue()).booleanValue();
//attribute fetch-group
n = fieldNode.getAttributes().getNamedItem("fetch-group");
if (n != null)
f.fetchGroup = n.getNodeValue();
//<!ATTLIST field fetch-depth CDATA #IMPLIED>
n = fieldNode.getAttributes().getNamedItem("fetch-depth");
if (n == null) {
n = fieldNode.getAttributes().getNamedItem("depth");
if (n != null) {
logger.log(BasicLevel.WARN, "attribute 'depth' is deprecated, use 'fetch-depth'.");
}
}
if (n != null) {
f.depth = Integer.valueOf(n.getNodeValue()).intValue();
}
//<!ATTLIST field embedded (true|false) #IMPLIED>
n = fieldNode.getAttributes().getNamedItem("embedded");
if (n != null)
f.embedded = Boolean.valueOf(n.getNodeValue()).booleanValue();
//<!ATTLIST field value-strategy CDATA #IMPLIED>
n = fieldNode.getAttributes().getNamedItem("value-strategy");
if (n != null)
f.valueStrategy = n.getNodeValue();
//<!ATTLIST field sequence CDATA #IMPLIED>
n = fieldNode.getAttributes().getNamedItem("sequence");
if (n != null)
f.sequence = n.getNodeValue();
//<!ATTLIST field mapped-by CDATA #IMPLIED>
n = fieldNode.getAttributes().getNamedItem("mapped-by");
if (n != null) {
f.reverseField = n.getNodeValue();
f.mappedByReversefield = true;
f.isCoherentReverseField = true;
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "The field " + f.name
+ " is mapped by " + f.reverseField);
}
}
}
fc.field = cf;
//<!ATTLIST field table CDATA #IMPLIED>
fc.parseTableAttribute(fieldNode, logger);
//<!ELEMENT field (extension*, (array|collection|map)?, join?,
//embedded?, element?, key?, value?, order?, column*, foreign-key?,
//index?, unique?, extension*)>
fc.fieldChildren = groupChildrenByName(fieldNode);
List l;
treatExtensions((List) fc.fieldChildren.get("extension"), cf);
if (fc.parseORM()) {
//Parse the O/R mapping of the [inherited] field
l = (List) fc.fieldChildren.get("join");
if (l != null) {
//define an external/secondary table used by a particular field
Object[] os = getJoin((Node) l.get(0), fc.join);
fc.join = (SpeedoJoin) os[0];
if (f != null) {
logger.log(BasicLevel.DEBUG, "Specify the join " + fc.join
+ " to the field " + f.name);
} else if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, "Specify the join " + fc.join
+ " to the inherited field " + cf.name);
}
}
//<!ATTLIST field column CDATA #IMPLIED>
n = fieldNode.getAttributes().getNamedItem("column");
if (n != null) {
fc.addColumn(new SpeedoColumn(n.getNodeValue()));
}
l = (List) fc.fieldChildren.get("column");
if (l != null) {
for (int i = 0; i < l.size(); i++) {
fc.addColumn(getColumn((Node) l.get(i)));
}
}
}
boolean isCollection = fc.fieldChildren.get("collection") != null
|| fc.fieldChildren.get("element") != null
;
boolean isMap = fc.fieldChildren.get("map") != null
|| fc.fieldChildren.get("key") != null
|| fc.fieldChildren.get("value") != null
;
boolean isArray = fc.fieldChildren.get("array") != null;
if (isMap) {
if (isCollection) {
logger.log(BasicLevel.WARN, "The '" + f.name
+ "' from the class '" + moClass.getFQName()
+ "' cannot be a map AND a collection !");
}
}
if (isCollection) {
treatCollectionField(fc);
} else if (isMap) {
treatMapField(fc);
} else if (isArray) {
} else {
treatSimpleField(fc);
}
unmanaged(new String[]{"serialized", "dependent", "delete-action",
"load-fetch-group", "foreign-key", "indexed", "unique"},
new String[]{"embedded", "order", "foreign-key", "unique",
"index", "array"},
fieldNode, fc.fieldChildren);
if (f != null) {
if (debug) {
logger.log(BasicLevel.DEBUG, "New field: "
+ "name=" + f.name
+ " / persistenceModifier = " + f.persistenceStatus
+ " / primaryKey = " + f.primaryKey
+ " / nullValue = " + f.nullValue
+ " / defaultFetchGroup = " + f.defaultFetchGroup
+ " / embedded = " + f.embedded
+ " / depth = " + f.depth
+ " / valueStrategy = " + f.valueStrategy
+ " / sequence = " + f.sequence
);
}
moClass.add(f, true, logger);
} //else it is an inherited field already attached to moClass.inheritance
}
private void treatMapField(FieldContext fc) throws SpeedoException {
SpeedoMap m = (SpeedoMap) fc.field.jdoTuple;
if (m == null) {
m = new SpeedoMap();
}
fc.field.jdoTuple = m;
m.moField = (SpeedoField) fc.field;
List l = (List) fc.fieldChildren.get("map");
if (l != null) {
l = (List) fc.fieldChildren.get("map");
Node mapNode = (Node) l.get(0);
//attribut key-type
Node n = mapNode.getAttributes().getNamedItem("key-type");
if (n != null)
m.keyType = n.getNodeValue();
//attribut embedded-key
n = mapNode.getAttributes().getNamedItem("embedded-key");
if (n != null)
m.embeddedKey = Boolean.valueOf(n.getNodeValue()).booleanValue();
//attribute value-type
n = mapNode.getAttributes().getNamedItem("value-type");
if (n != null)
m.valueType = n.getNodeValue();
//attribute embedded-value
n = mapNode.getAttributes().getNamedItem("embedded-value");
if (n != null)
m.embeddedValue = Boolean.valueOf(n.getNodeValue()).booleanValue();
}
l = (List) fc.fieldChildren.get("key");
if (l != null) {
//<!ELEMENT key (extension*, embedded?, column*, foreign-key?, index?, unique?, extension*)>
//<!ATTLIST key table CDATA #IMPLIED>
//<!ATTLIST key serialized (true|false) #IMPLIED>
//<!ATTLIST key delete-action (restrict|cascade|null|default|none) #IMPLIED>
//<!ATTLIST key indexed (true|false|unique) #IMPLIED>
//<!ATTLIST key unique (true|false) #IMPLIED>
Node keyNode = (Node) l.get(0);
Map keyChildren = groupChildrenByName(keyNode);
if (fc.parseORM()) {
//<!ATTLIST key column CDATA #IMPLIED>
Node n = keyNode.getAttributes().getNamedItem("column");
if (n != null) {
SpeedoColumn col = new SpeedoColumn(n.getNodeValue());
//in the same table than map value
if (fc.field.columns != null && fc.field.columns.length > 0) {
col.table = fc.field.columns[0].table;
}
m.keyColumns = col;
}
l = (List) keyChildren.get("column");
if (l != null) {
m.keyColumns = getColumn((Node) l.get(0));
}
fc.parseTableAttribute(keyNode, logger);
}
unmanaged(
new String[]{"serialized", "delete-action", "indexed", "unique"},
new String[]{"embedded", "foreign-key", "unique", "index"},
keyNode, keyChildren);
}
l = (List) fc.fieldChildren.get("value");
if (l != null) {
//<!ELEMENT value (extension*, embedded?, column*, foreign-key?, index?, unique?, extension*)>
//<!ATTLIST value serialized (true|false) #IMPLIED>
//<!ATTLIST value table CDATA #IMPLIED>
//<!ATTLIST value delete-action (restrict|cascade|null|default|none) #IMPLIED>
//<!ATTLIST value indexed (true|false|unique) #IMPLIED>
//<!ATTLIST value unique (true|false) #IMPLIED>
Node valueNode = (Node) l.get(0);
Map valueChildren = groupChildrenByName(valueNode);
if (fc.parseORM()) {
//<!ATTLIST value column CDATA #IMPLIED>
Node n = valueNode.getAttributes().getNamedItem("column");
if (n != null) {
fc.addColumn(new SpeedoColumn(n.getNodeValue()));
}
l = (List) valueChildren.get("column");
if (l != null) {
for (int i = 0; i < l.size(); i++) {
fc.addColumn(getColumn((Node) l.get(i)));
}
}
fc.parseTableAttribute(valueNode, logger);
}
unmanaged(
new String[]{"serialized", "delete-action", "indexed", "unique"},
new String[]{"embedded", "foreign-key", "unique", "index"},
valueNode, valueChildren);
}
if (fc.join != null && fc.field.moClass.containsJoin(fc.join)) {
fc.field.moClass.removeJoin(fc.join);
}
logger.log(BasicLevel.DEBUG, "New map: "
+ "keyType=" + m.keyType
+ " / embeddedKey = " + m.embeddedKey
+ " / valueType = " + m.valueType
+ " / embeddedValue = " + m.embeddedValue
);
}
private void treatCollectionField(FieldContext fc) throws SpeedoException {
List l = (List) fc.fieldChildren.get("collection");
if (l != null) {
Node collectionNode = (Node) l.get(0);
SpeedoCollection co = (SpeedoCollection) fc.field.jdoTuple;
if (co == null) {
co = new SpeedoCollection();
}
fc.field.jdoTuple = co;
co.moField = (SpeedoField) fc.field;
//attribute element-type
Node n = collectionNode.getAttributes().getNamedItem("element-type");
if (n != null)
co.elementType = n.getNodeValue();
//attribute embedded-element
n = collectionNode.getAttributes().getNamedItem("embedded-element");
if (n != null) {
co.embeddedElement = Boolean.valueOf(n.getNodeValue()).booleanValue();
}
if (debug) {
logger.log(BasicLevel.DEBUG, "New collection: "
+ "elementType=" + co.elementType
+ " / embeddedElement = " + co.embeddedElement
);
}
}
l = (List) fc.fieldChildren.get("element");
if (l != null) {
//<!ATTLIST element column CDATA #IMPLIED>
Node elementNode = (Node) l.get(0);
//<!ELEMENT element (extension*, embedded?, column*, foreign-key?,
// index?, unique?, extension*)>
Map elementChildren = groupChildrenByName(elementNode);
if (fc.parseORM()) {
Node n = elementNode.getAttributes().getNamedItem("column");
if (n != null) {
fc.addColumn(new SpeedoColumn(n.getNodeValue()));
}
l = (List) elementChildren.get("column");
if (l != null) {
for (int i = 0; i < l.size(); i++) {
fc.addColumn(getColumn((Node) l.get(i)));
}
}
//<!ATTLIST element table CDATA #IMPLIED>
n = elementNode.getAttributes().getNamedItem("table");
if (n != null) {
if (fc.table == null) {
fc.table = new SpeedoTable();
} else {
logger.log(BasicLevel.WARN, "Specify two tables for the "
+ fc.field.getSourceDesc()
+ ": " + fc.table.name + " and " + n.getNodeValue());
}
fc.table.name = n.getNodeValue();
}
}
unmanaged(new String[]{"serialized", "delete-action",
"update-action", "indexed", "unique"},
new String[]{"embedded", "foreign-key", "unique", "index"},
elementNode, fc.fieldChildren);
}
if (!fc.parseORM()) {
return;
}
if (fc.join != null && fc.field.moClass.containsJoin(fc.join)) {
fc.field.moClass.removeJoin(fc.join);
}
if (fc.table != null || fc.join != null) {
// The collection is stored in a join table
if (fc.join == null) {
// the user specified a table without join. Maybe the join to
// this table is already defined at class level
SpeedoJoin[] js = fc.field.moClass.joinToExtTables;
if (js != null) {
for (int i = 0; i < js.length; i++) {
if (js[i].extTable != null && fc.table.name.equals(js[i].extTable.name)) {
//The join to this table is already defined at class level
// use it
fc.field.join = js[i];
i = js.length;
}
}
if (fc.field.join == null) {
//no existing join found, create a new one
fc.join = new SpeedoJoin();
}
}
}
fc.field.join = fc.join;
fc.field.join.extTable = fc.table;
fc.field.join.mainTable = fc.field.moClass.mainTable;
} else {
// The collection is stored in the table of the referenced state
fc.field.join = new SpeedoJoin();
}
fc.field.columns = fc.getColumns();
}
private void treatSimpleField(FieldContext fc) {
if (!fc.parseORM()) {
return;
}
//The field is a primitive field OR a mono valued reference to
// another persistent class
if (fc.join != null) { //join sub element specified
if (fc.table == null) { //no table name specified in field
fc.table = new SpeedoTable();
//allocate a defaut name for the secondary table
fc.table.name = fc.field.moClass.name.toUpperCase()
+ "_" + fc.field.name.toUpperCase();
}
fc.field.join = fc.join;
fc.field.join.extTable = fc.table;
fc.field.join.mainTable = fc.field.moClass.mainTable;
if (!fc.field.moClass.containsJoin(fc.field.join)) {
fc.field.moClass.addJoin(fc.field.join);
}
} else if (fc.table != null) {
if (fc.field.moClass.mainTable != null
&& fc.table.name.equals(fc.field.moClass.mainTable.name)) {
//the user specified the same table than the main table
// without join.
fc.table = null;
}
if (fc.table != null) {
logger.log(BasicLevel.INFO,
"The user specified a secondary table without join columns");
if (fc.field.moClass.getExtTable(fc.table.name, false) != fc.table) {
logger.log(BasicLevel.INFO,
"The user specified a secondary table without join columns");
fc.field.join = new SpeedoJoin();
fc.field.join.mainTable = fc.field.moClass.mainTable;
fc.field.join.extTable = fc.table;
fc.field.moClass.addJoin(fc.field.join);
} else {
logger.log(BasicLevel.DEBUG, "Field '" + fc.field.name
+ " is mapped into the table '"
+ fc.table.name + "'.");
}
}
}
if (fc.table == null) {
fc.table = fc.field.moClass.mainTable;
}
if (fc.columns != null) {
for (int i = 0; i < fc.columns.size(); i++) {
SpeedoColumn column = (SpeedoColumn) fc.columns.get(i);
if (column.table == null) {
column.table = fc.table;
}
fc.field.addColumn(column);
logger.log(BasicLevel.DEBUG, "specify column " + column.name
+ " to the field " + fc.field.name + ": " + fc.field.columns);
}
} else {
//no column specified, the mapping will be defined later
// when we will have more information about the field type.
}
}
private void treatFetchGroupNode(Node fetchNode, SpeedoClass moClass) throws SpeedoException {
SpeedoFetchGroup fg = treatFetchGroupTree(fetchNode, moClass);
moClass.fetchGroups.put(fg.name, fg);
}
private SpeedoFetchGroup treatFetchGroupTree(Node fetchNode,
SpeedoClass fieldOwner) throws SpeedoException {
//<!ELEMENT fetch-group (fetch-group|field)*>
SpeedoFetchGroup fg = new SpeedoFetchGroup();
//<!ATTLIST fetch-group name CDATA #REQUIRED>
fg.name = getStringAttributeValue(fetchNode, "name", null);
//<!ATTLIST fetch-group post-load (true|false) #IMPLIED>
fg.postLoad = getBooleanAttributeValue(fetchNode, "post-load", false);
Map fgChildren = groupChildrenByName(fetchNode);
List fgs = (List) fgChildren.get("fetch-group");
if (fgs != null) {
for (Iterator fgIt = fgs.iterator(); fgIt.hasNext();) {
Node fgNode = (Node) fgIt.next();
fg.addFetchGroup(treatFetchGroupTree(fgNode, fieldOwner));
}
}
List fields = (List) fgChildren.get("field");
if (fields != null) {
for (Iterator fieldIt = fields.iterator(); fieldIt.hasNext();) {
Node fieldNode = (Node) fieldIt.next();
SpeedoField sf = new SpeedoField();
sf.name = getStringAttributeValue(fieldNode, "name", null);
fg.addField(sf);
//depth for a field
String depth = getStringAttributeValue(fieldNode, "fetch-depth", null);
if (depth == null) {
depth = getStringAttributeValue(fieldNode, "depth", null);
logger.log(BasicLevel.WARN, "attribute 'depth' is deprecated, use 'fetch-depth'.");
}
if (depth != null) {
sf.depth = new Integer(depth).intValue();
}
}
}
return fg;
}
private static String getStringAttributeValue(Node node, String attribName, String defaultValue) {
Node n = node.getAttributes().getNamedItem(attribName);
if (n != null) {
return n.getNodeValue();
} else {
return defaultValue;
}
}
private static boolean getBooleanAttributeValue(Node node, String attribName, boolean defaultValue) {
Node n = node.getAttributes().getNamedItem(attribName);
if (n != null) {
return Boolean.valueOf(n.getNodeValue()).booleanValue();
} else {
return defaultValue;
}
}
private void unmanaged(String[] attributeNames,
String[] nodeTypes,
Node node,
Map type2nodes) {
String parent = node.getNodeName();
if (attributeNames != null) {
for (int i = 0; i < attributeNames.length; i++) {
Node n = node.getAttributes().getNamedItem(attributeNames[i]);
if (n != null) {
logger.log(BasicLevel.WARN, "Speedo does not support yet the "
+ attributeNames[i] + " attribute in "
+ parent + " tag.");
}
}
}
if (nodeTypes != null) {
for (int i = 0; i < nodeTypes.length; i++) {
List l = (List) type2nodes.get(nodeTypes[i]);
if (l != null) {
for (int j = 0; j < l.size(); j++) {
logger.log(BasicLevel.WARN, "Speedo does not support yet the "
+ nodeTypes[i] + " sub element in "
+ parent + " tag.");
}
}
}
}
}
private static class FieldContext {
/**
* The Speedo meta object representing the field:
* SpeedoField or SpeedoInheritedField
*/
public SpeedoCommonField field;
/**
* The join found associated to this field
*/
public SpeedoJoin join;
/**
* The table instance
*/
public SpeedoTable table;
public List columns;
public List idxColumns;
public Node fieldNode;
public Map fieldChildren;
public void addColumn(SpeedoColumn col) {
if (columns == null) {
columns = new ArrayList();
}
columns.add(col);
}
public boolean parseORM() {
return !(field instanceof SpeedoField)
|| !((SpeedoField) field).mappedByReversefield;
}
public void parseTableAttribute(Node node, Logger logger) {
Node n = fieldNode.getAttributes().getNamedItem("table");
if (n != null) {
join = field.moClass.getJoin(n.getNodeValue(), true);
table = join.extTable;
if (logger.isLoggable(BasicLevel.DEBUG)) {
if (field instanceof SpeedoField) {
logger.log(BasicLevel.DEBUG, "specify table " + table.name
+ " to the field " + field.name + ": " + field.columns);
} else {
logger.log(BasicLevel.DEBUG, "specify table " + table.name
+ " to the inherited field " + field.name
+ ": " + field.columns);
}
}
}
}
public SpeedoColumn[] getColumns() {
if (columns == null || columns.size() == 0) {
return null;
} else {
return (SpeedoColumn[]) columns.toArray(new SpeedoColumn[columns.size()]);
}
}
}
}